Skip to content

Commit

Permalink
[opt] Allow more inlining of class methods (#366)
Browse files Browse the repository at this point in the history
  • Loading branch information
titzer authored Feb 27, 2025
1 parent ce68ae4 commit 6c0f39b
Show file tree
Hide file tree
Showing 20 changed files with 175 additions and 14 deletions.
2 changes: 1 addition & 1 deletion aeneas/src/ir/Facts.v3
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ enum Fact {
M_EQUALS, // method is an equality comparator
M_OVERRIDDEN, // method has been overridden in a subclass
M_OVERRIDE, // method overrides a superclass method
M_OPERATOR, // method wraps an operator
M_ENUM_INIT, // method represents a enum init initializer
M_NEW, // the method is a constructor
M_ABSTRACT, // the method is abstract
M_INLINE, // method should be inlined whenever possible
Expand Down
2 changes: 1 addition & 1 deletion aeneas/src/ir/IrOpMethodBuilder.v3
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class IrOpMethodBuilder(prog: Program) {
def receiver = createGlobalIrClass();
var context = SsaContext.new(compiler, prog);
var meth = createIrMethod(receiver, typeArgs, op.sig);
meth.setFact(Fact.M_INLINE | Fact.M_OPERATOR);
meth.setFact(Fact.M_INLINE);
context.enterMethod(meth);
var block = createSsa(context, receiver, meth);
// build block
Expand Down
7 changes: 5 additions & 2 deletions aeneas/src/ir/Normalization.v3
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def defaultGetBitWidth(compiler: Compiler, prog: Program, t: Type) -> byte {
}

// Normalizes a program based on the results of reachability analysis.
def TRANSFERRABLE_FACTS = (Fact.M_ABSTRACT | Fact.M_INLINE | Fact.M_OPERATOR | Fact.M_NEW | Fact.M_EMPTY | Fact.M_EQUALS);
def TRANSFERRABLE_FACTS = (Fact.M_ABSTRACT | Fact.M_INLINE | Fact.M_ENUM_INIT | Fact.M_NEW | Fact.M_EMPTY | Fact.M_EQUALS);
class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) {
def liveClasses = Vector<RaClass>.new();
def context = SsaContext.new(ra.compiler, ra.prog);
Expand Down Expand Up @@ -177,7 +177,10 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer)
}

def norm(t: Type) -> TypeNorm {
if (t.open()) return V3.fail1("is open %q", t.render);
if (t.open()) {
context.fail1("is open %q", t.render);
return null;
}
var tn = typeMap[t];
if (tn != null) return tn;
// not in the hashmap, build appropriately
Expand Down
2 changes: 1 addition & 1 deletion aeneas/src/main/Compiler.v3
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ class Compilation(compiler: Compiler, prog: Program) {
var graph = gen.generate();
context.verify();
meth.ssa = graph;
if (gen.directCallBlocks != null) {
if (gen.directCallBlocks != null && !meth.facts.M_ENUM_INIT) { // don't early-inline into enum inits
// Perform inlining.
// TODO: -inline=foo also enables heuristic-based early inlining
SsaEarlyInliner.new(context, this, gen).inline(depth);
Expand Down
2 changes: 1 addition & 1 deletion aeneas/src/main/Version.v3
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@

// Updated by VCS scripts. DO NOT EDIT.
component Version {
def version: string = "III-9.1806";
def version: string = "III-9.1807";
var buildData: string;
}
7 changes: 6 additions & 1 deletion aeneas/src/ssa/SsaBuilder.v3
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,12 @@ class SsaBuilder {
def opCallVirtual(m: IrSpec, x: Array<SsaInstr>) -> SsaInstr {
var facts = m.member.facts & Fact.O_PURE;
if(x[0].facts.V_NON_ZERO) facts |= Fact.O_NO_NULL_CHECK;
return add(V3Op.bestCallVirtual(m), x, facts); // XXX: devirtualize based on x's type as well
var op = V3Op.bestCallVirtual(m);
match (op.opcode) {
CallClassMethod(m) => recordDirectCall(V3Op.extractIrSpec(op, m));
_ => ;
}
return add(op, x, facts); // XXX: devirtualize based on x's type as well
}
// CallClosure(args)
def opCallClosure(ftype: Type, x: Array<SsaInstr>) -> SsaInstr {
Expand Down
13 changes: 7 additions & 6 deletions aeneas/src/ssa/SsaOptimizer.v3
Original file line number Diff line number Diff line change
Expand Up @@ -818,8 +818,10 @@ class SsaInstrReducer(context: SsaContext) extends SsaInstrMatcher {
if (field.isConst()) {
if (SsaConst.?(receiver)) {
// ClassGetField(#K) => #K
return graph.valConst(getFieldType(i.op, field),
asRecord(receiver).values[field.index]);
var record = asRecord(receiver);
var ftype = field.fieldType;
if (ftype.open()) ftype = IrSpec.new(record.rtype, TypeUtil.NO_TYPES, field).getFieldType();
return graph.valConst(ftype, record.values[field.index]);
}
i.setFactIf(Fact.O_NO_NULL_CHECK, Fact.O_PURE);
i.facts |= Fact.F_VALUE;
Expand Down Expand Up @@ -868,10 +870,9 @@ class SsaInstrReducer(context: SsaContext) extends SsaInstrMatcher {
if (SsaConst.?(receiver)) {
// VariantGetField(#K) => #K
var record = asRecord(receiver);
var val = if(record != null, record.values[field.index]);
//TODO: variants should not have null records, but this can currently happen in folding during normalization
//TODO if (record == null) context.fail1("VariantGetField @%d has null input record", i.uid);
return graph.valConst(getFieldType(i.op, field), val);
var ftype = field.fieldType;
if (ftype.open()) ftype = IrSpec.new(record.rtype, TypeUtil.NO_TYPES, field).getFieldType();
return graph.valConst(ftype, if(record != null, record.values[field.index]));
}
if (!i.facts.F_POINTED_AT) i.facts |= Fact.F_VALUE;
if (optimize_loads) return state.load(receiver, field, i);
Expand Down
5 changes: 4 additions & 1 deletion aeneas/src/ssa/VstSsaGen.v3
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ class VstSsaGen extends VstVisitor<VstSsaEnv, SsaInstr> {
return graph;
}
def addConstructorCode(decl: VstNew, env: VstSsaEnv) {
if (decl.receiver.isEnum()) return addEnumParamInitCode(env);
if (decl.receiver.isEnum()) {
context.method.facts |= Fact.M_ENUM_INIT;
return addEnumParamInitCode(env);
}
appendImplicitFieldInits(decl.params.list, env);
appendFieldInits(decl.memberinits, env);
appendSuperClause(decl.superclause, env);
Expand Down
10 changes: 10 additions & 0 deletions test/core/inline_cm00.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//@execute 0=0; 111=111
class C {
def m() -> int { return 0; }
}

def c = C.new();

def main(a: int) -> int {
return a + c.m();
}
10 changes: 10 additions & 0 deletions test/core/inline_cm01.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//@execute 0=100; 11=111
class C {
def m() -> int { return 100; }
}

def c = C.new();

def main(a: int) -> int {
return a + c.m();
}
13 changes: 13 additions & 0 deletions test/core/inline_cm02.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@execute 0=200; 11=211
class C {
def m() -> int { return 200; }
}

class D extends C {
}

def c = C.new();

def main(a: int) -> int {
return a + c.m();
}
14 changes: 14 additions & 0 deletions test/core/inline_cm03.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@execute 0=200; 11=211
class C {
def m() -> int { return 200; }
}

class D extends C {
def m() -> int { return 300; }
}

def c = C.new();

def main(a: int) -> int {
return a + c.m();
}
13 changes: 13 additions & 0 deletions test/core/inline_cm04.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@execute 0=200; 11=211
class C {
def m() -> int { return 200; }
}

class D extends C {
}

def c: C = D.new();

def main(a: int) -> int {
return a + c.m();
}
13 changes: 13 additions & 0 deletions test/core/inline_cm05.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@execute 0=200; 11=211
class C {
def m() -> int { return 200; }
}

class D extends C {
}

def d: D = D.new();

def main(a: int) -> int {
return a + d.m();
}
12 changes: 12 additions & 0 deletions test/core/inline_cm06.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//@execute = false
class C {
def m() -> bool { return D.?(this); }
}
class D extends C {
}

def c = C.new();

def main() -> bool {
return c.m();
}
12 changes: 12 additions & 0 deletions test/core/inline_cm07.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//@execute = true
class C {
def m() -> bool { return D.?(this); }
}
class D extends C {
}

def d = D.new();

def main() -> bool {
return d.m();
}
12 changes: 12 additions & 0 deletions test/core/inline_cm08.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//@execute 0=false; 1=true
class C {
def m() -> bool { return D.?(this); }
}
class D extends C {
}

def a = [C.new(), D.new()];

def main(x: int) -> bool {
return a[x].m();
}
13 changes: 13 additions & 0 deletions test/core/inline_cm09.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@execute = true
class C {
def m() -> bool { return D.?(this); }
}
class D extends C {
def m() -> bool { return D.?(this); }
}

def d = D.new();

def main() -> bool {
return d.m();
}
13 changes: 13 additions & 0 deletions test/core/inline_poly00.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@execute = true
class C<T> {
def x: T;
def m() -> C<T> {
return C<T>.new();
}
}

def main() -> bool {
var c = C<int>.new();
var d = c.m();
return c.x == d.x;
}
14 changes: 14 additions & 0 deletions test/enums/param08.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@execute = 33
def sum = E.A.x + E.B.x;

def f() {
}

enum E(x: int, v: void) {
A(11, f()),
B(22, f())
}

def main() -> int {
return sum;
}

0 comments on commit 6c0f39b

Please sign in to comment.