Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aeneas/src/core/Opcode.v3
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ component Opcodes {
t[Opcode.CallerIp.tag] = P;
t[Opcode.CallerSp.tag] = P;

t[Opcode.Alloc.tag] = NONE;
t[Opcode.Alloc.tag] = NZ; // XXX: dead-code eliminatable

t[Opcode.RefLayoutAt.tag] = NZ|F;
t[Opcode.RefLayoutIn.tag] = P|F;
Expand Down
880 changes: 880 additions & 0 deletions aeneas/src/core/Opcode2.v3

Large diffs are not rendered by default.

334 changes: 158 additions & 176 deletions aeneas/src/core/Operator.v3

Large diffs are not rendered by default.

346 changes: 346 additions & 0 deletions aeneas/src/core/Operator2.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
// Copyright 2024 Virgil authors. All rights reserved.
// See LICENSE for details of Apache 2.0 license.

class Operator2(opcode: Opcode2) {
private var openness: Open; // lazily computed open / closed
private var sig_: Signature;

def sig() -> Signature {
return if(sig_ != null, sig_, makeSig().sig_);
}
def subst(func: Type -> Type) -> Operator2 {
if (openness == Open.CLOSED) return this;
if (openness == Open.UNKNOWN) openness = Opcodes2.checkOpenness(opcode);
if (openness == Open.CLOSED) return this;
return Operator2.new(Opcodes2.subst(opcode, func));
}
private def makeSig() -> this {
match (opcode) {
// Boolean operators
BoolEq => set(TYPE_zz, arr_zz, TYPE_z, arr_z);
BoolAnd => set(TYPE_zz, arr_zz, TYPE_z, arr_z);
BoolOr => set(TYPE_zz, arr_zz, TYPE_z, arr_z);
BoolNot => set(TYPE_z, arr_z, TYPE_z, arr_z);
// Integer arithmetic and conversions
IntEq(t) => set_xx_z(t);
IntAdd(t) => set_xx_x(t);
IntSub(t) => set_xx_x(t);
IntMul(t) => set_xx_x(t);
IntDiv(t) => set_xx_x(t);
IntMod(t) => set_xx_x(t);
IntAnd(t) => set_xx_x(t);
IntOr(t) => set_xx_x(t);
IntXor(t) => set_xx_x(t);
IntShl(t) => set_xb_x(t);
IntSar(t) => set_xb_x(t);
IntShr(t) => set_xb_x(t);
IntLt(t) => set_xx_z(t);
IntLteq(t) => set_xx_z(t);
IntWide(op, p, r) => set_arr(p, r);
// Floating point arithmetic
FloatAdd(t) => set_xx_x(t);
FloatSub(t) => set_xx_x(t);
FloatMul(t) => set_xx_x(t);
FloatDiv(t) => set_xx_x(t);
FloatBitEq(t) => set_xx_z(t);
FloatEq(t) => set_xx_z(t);
FloatNe(t) => set_xx_z(t);
FloatLt(t) => set_xx_z(t);
FloatLteq(t) => set_xx_z(t);
FloatAbs(t) => set_x_x(t);
FloatCeil(t) => set_x_x(t);
FloatFloor(t) => set_x_x(t);
FloatSqrt(t) => set_x_x(t);
// Integer casts and conversions
IntCastF(to, from) => set_x_y(from, to);
IntQueryF(to, from) => set_x_z(from);
IntViewI(to, from) => set_x_y(from, to);
IntViewF(to, from) => set_x_y(from, to);
IntTruncF(to, from) => set_x_y(from, to);
// Floating point casts and conversions
FloatCastI(to, from) => set_x_y(from, to);
FloatCastD(to, from) => set_x_y(from, to);
FloatQueryI(to, from) => set_x_z(from);
FloatQueryD(to, from) => set_x_z(from);
FloatPromoteI(to, from) => set_x_y(from, to);
FloatPromoteF(to, from) => set_x_y(from, to);
FloatViewI(to, from) => set_x_y(from, to);
FloatRoundI(to, from) => set_x_y(from, to);
FloatRound(t) => set_x_y(t, t);
FloatRoundD(to, from) => set_x_y(from, to);
// Reference equality
RefEq(t) => set_xx_z(t);
// Default value operator
DefaultValue(t) => set(TYPE_v, arr_v, t, [t]);
IntRepCreate(to, from) => set_x_y(from, to);
IntRepView(to, from) => set_x_y(from, to);
// Tuple operations
TupleCreate(t) => {
var paramTypes = Lists.toArray(t.nested);
set(t, paramTypes, t, [t]);
}
TupleGetElem(t, index) => {
var e = Lists.get(t.nested, index);
set(t, [t], e, [e]);
}
// Array operations
ArrayAlloc(t) => set_x_y(TYPE_i, t);
ArrayInit(t, length) => {
var elemType = t.elementType();
var paramTypes = Array<Type>.new(length);
for (i < paramTypes.length) paramTypes[i] = elemType;
set(Tuple.fromTypeArray(paramTypes), paramTypes, t, [t]);
}
ArrayFill(t) => {
var elemType = t.elementType();
var paramTypes = [t, elemType];
set(Tuple.fromTypeArray(paramTypes), paramTypes, t, [t]);
}
ArrayTupleInit(t, elems, length) => {
var elemType = t.elementType();
var paramTypes = Array<Type>.new(elems * length);
var tuple = Tuple.toTypeArray(elemType);
var i = 0;
for (j < length) {
for (k < tuple.length) {
paramTypes[i++] = tuple[k];
}
}
set(Tuple.fromTypeArray(paramTypes), paramTypes, t, [t]);
}
ArrayGetElem(t, it) => set_arr([t, it], [t.elementType()]);
ArraySetElem(t, it) => set_arr([t, it, t.elementType()], arr_v);
ArrayGetElemElem(t, it, index) => {
var etype = Tuple.elementType(t.elementType(), index);
var paramTypes = [t, it];
set(Tuple.fromTypeArray(paramTypes), paramTypes, etype, [etype]);
}
ArraySetElemElem(t, it, index) => {
var etype = Tuple.elementType(t.elementType(), index);
var tt = [t, it, etype];
set(Tuple.fromTypeArray(tt), tt, TYPE_v, arr_v);
}
ArrayGetLength(t) => set_x_y(t, TYPE_i);
// Range operations
RangeFromTo(t, st, et) => set_arr([t, st, et], [t]);
RangeFromPlus(t, st, lt) => set_arr([t, st, lt], [t]);
RangeGetElem(t, it) => set_arr([t, it], [t.elementType()]);
RangeSetElem(t, it) => set_arr([t, it, t.elementType()], arr_v);
RangeGetLength(t) => set_x_y(t, TYPE_i);
RangeStartPlusIndex(t, it) => set_arr([TYPE_rs, it], arr_rs);
RangeStartFromPointer(t, pt) => set_arr([pt], arr_rs);
// Normalized Range operations
NormRangeGetElem(t, it) => set_arr([t, TYPE_rs, it], [t.elementType()]);
NormRangeGetElemElem(t, index, it) => set_arr([t, TYPE_rs, it], [Tuple.elementType(t.elementType(), index)]);
NormRangeSetElem(t, it) => set_arr([t, TYPE_rs, it, t.elementType()], arr_v);
NormRangeSetElemElem(t, index, it) => set_arr([t, TYPE_rs, it, Tuple.elementType(t.elementType(), index)], arr_v);
// Component operations
Init(method) => set(TYPE_v, arr_v, TYPE_v, arr_v);
ComponentGetField(field) => set(field.receiver, [field.receiver], field.fieldType, [field.fieldType]);
ComponentSetField(field) => set_arr([field.receiver, field.fieldType], arr_v);
// Class operations
ClassAlloc(spec) => {
var ftype = spec.getBoundType(), p = Function.getParamType(ftype);
set(p, Tuple.toTypeArray(p), spec.receiver, [spec.receiver]);
}
ClassEmptyAlloc(classType, paramTypes) => {
set(Tuple.fromTypeArray(paramTypes), paramTypes, classType, [classType]);
}
ClassGetField(spec) => {
var r = spec.receiver, f = spec.getFieldType();
set(r, [r], f, Tuple.toTypeArray(f));
}
ClassInitField(spec) => {
var r = spec.receiver, f = spec.getFieldType(), arr = [r, f];
set(Tuple.fromTypeArray(arr), arr, TYPE_v, arr_v);
}
ClassSetField(spec) => {
var r = spec.receiver, f = spec.getFieldType(), arr = [r, f];
set(Tuple.fromTypeArray(arr), arr, TYPE_v, arr_v);
}
ClassGetMethod(spec) => {
var r = spec.receiver, f = spec.getBoundType();
set_x_y(r, f);
}
ClassGetVirtual(spec) => {
var r = spec.receiver, f = spec.getBoundType();
set_x_y(r, f);
}
ClassGetSelector(spec) => {
var r = spec.receiver, f = spec.getFuncType();
set_x_y(r, f);
}
// Variant operations
VariantEq(t) => set_xx_z(t);
VariantAlloc(classType, paramTypes) => {
set(Tuple.fromTypeArray(paramTypes), paramTypes, classType, [classType]);
}
VariantGetTag(t) => set_x_y(t, V3.getVariantTagType(t));
VariantGetField(spec) => {
var r = spec.receiver, f = spec.getFieldType();
set(r, [r], f, Tuple.toTypeArray(f));
}
VariantGetMethod(spec) => {
var r = spec.receiver, f = spec.getBoundType();
set_x_y(r, f);
}
VariantGetVirtual(spec) => {
var r = spec.receiver, f = spec.getBoundType();
set_x_y(r, f);
}
VariantGetSelector(spec) => {
var r = spec.receiver, f = spec.getFuncType();
set_x_y(r, f);
}
// Safety checks
NullCheck(t) => set(t, [t], TYPE_v, arr_v);
BoundsCheck(t) => set_arr([t, TYPE_i], arr_v);
ConditionalThrow(exception) => set(TYPE_z, arr_z, TYPE_v, arr_v);
// Overloaded, polymorphic casts
OverloadedEq(t) => set_xx_z(t);
TypeCast(cast, to, from) => set_x_y(from, to);
TypeQuery(query, to, from) => set_x_z(from);
TypeSubsume(to, from) => set_x_y(from, to);
// Closure and call operations
CallMethod(spec) => {
var ftype = FuncType.!(spec.getUnboundType());
set_ftype(ftype);
}
CallClassMethod(spec) => {
var ftype = FuncType.!(spec.getUnboundType());
set_ftype(ftype);
}
CallClassVirtual(spec) => {
var ftype = FuncType.!(spec.getUnboundType());
set_ftype(ftype);
}
CallClassSelector(spec) => {
var ftype = FuncType.!(spec.getUnboundType());
set_ftype(ftype);
}
CallVariantVirtual(spec) => {
var ftype = FuncType.!(spec.getUnboundType());
set_ftype(ftype);
}
CallVariantSelector(spec) => {
var ftype = FuncType.!(spec.getUnboundType());
set_ftype(ftype);
}
CallClosure(t) => {
var sig = t.sig();
set_arr(Arrays.prepend(t, sig.paramTypes), sig.returnTypes);
}
CallFunction(t) => {
var sig = t.sig(), ftype = t.prependParam(TYPE_r);
set_arr(Arrays.prepend(ftype, sig.paramTypes), sig.returnTypes);
}
CreateClosure(obj, method) => {
set_x_y(obj, method.getBoundType());
}
ForgeClosure(ptrType, closureType, paramType, resultType) => {
var funcType = Function.newType(paramType, resultType);
set_arr([ptrType, closureType], [funcType]);
}
UnpackClosure(ptrType, closureType, paramType, resultType) => {
var funcType = Function.newType(paramType, resultType);
set_arr([funcType], [ptrType, closureType]);
}
// RefLayout operations
RefLayoutAt(t) => set_arr([V3.arrayByteType, TYPE_i], [t]);
RefLayoutOf(t) => set_arr([V3.rangeByteType], [t]);
RefLayoutIn(t, offset, rt) => set_x_y(t, rt);
RefLayoutGetField(t, offset, ft) => set_x_y(t, ft);
RefLayoutSetField(t, offset, ft) => set_arr([t, ft], arr_v);
RefLayoutAtRepeatedField(t, offset, scale, max, rt) => set_arr([t, TYPE_i], [rt]);
RefLayoutGetRepeatedField(t, offset, scale, max, ft) => set_arr([t, TYPE_i], [ft]);
RefLayoutSetRepeatedField(t, offset, scale, max, ft) => set_arr([t, TYPE_i, ft], arr_v);
ByteArrayGetField(st, offset, ft) => set_arr([TYPE_ab, st], [ft]);
ByteArraySetField(st, offset, ft) => set_arr([TYPE_ab, st, ft], arr_v);
ForgeRange(ptrType, t) => set_arr([ptrType, TYPE_i], [t]);
// System operations
SystemCall(syscall, paramType, resultType) => set_ftype(FuncType.!(Function.newType(paramType, resultType)));
// Container for VST operations
VstSugar(op, paramTypes, resultType) => set_arr(paramTypes, [resultType]);
// Pointer operations
PtrAdd(t, it) => set_arr([t, it], [t]);
PtrSub(t, it) => set_arr([t, t], [it]);
PtrLt(t) => set_xx_z(t);
PtrLteq(t) => set_xx_z(t);
PtrAtContents(t, at) => set_x_y(at, t);
PtrAtLength(t, arrayType) => set_x_y(arrayType, t);
PtrAtObject(t, objType) => set_x_y(objType, t);
PtrAtRangeElem(t, rangeType, it) => set_arr([rangeType, it], [t]);
PtrAtArrayElem(t, arrayType, it) => set_arr([arrayType, it], [t]);
PtrAtEnd(t, objType) => set_x_y(objType, t);
PtrAtRef(t, refType) => set_x_y(refType, t);
PtrAtComponentField(t, field) => set(TYPE_v, arr_v, t, [t]);
PtrAtObjectField(t, field) => set_x_y(field.receiver, t);
PtrAtRefLayoutField(refType, t, offset) => set_x_y(refType, t);
PtrAtUnboxedObjectField(fields, ptrType) => set_x_y(fields.head.receiver, ptrType);
PtrAtUnboxedComponentField(fields, ptrType) => set_x_y(fields.head.receiver, ptrType);
PtrCmpSwp(t, valType) => set_arr([t, valType, valType], arr_z);
PtrLoad(t, valType) => set_x_y(t, valType);
PtrStore(t, valType) => set_arr([t, valType], arr_v);
PtrAddRangeStart(t) => set_arr([t, TYPE_rs], [t]);
// Get caller instruction pointer or stack pointer
CallerIp(t) => set(TYPE_v, arr_v, t, [t]);
CallerSp(t) => set(TYPE_v, arr_v, t, [t]);
// Allocate raw memory
Alloc(t) => set(TYPE_v, arr_v, t, [t]);
// Call
CallAddress(p, rep) => {
set_arr(rep.paramTypes, rep.returnTypes);
}
CallKernel(kernel, paramTypes, resultType) => {
set(Tuple.fromTypeArray(paramTypes), paramTypes, resultType, Tuple.toTypeArray(resultType));
}
}
}
def isPolymorphic() -> bool {
if (openness == Open.CLOSED) return false;
if (openness == Open.UNKNOWN) openness = Opcodes2.checkOpenness(opcode);
return openness != Open.CLOSED;
}
private def set_x_x(x: Type) {
var arr_x = [x];
set(x, arr_x, x, arr_x);
}
private def set_x_y(x: Type, y: Type) {
set(x, [x], y, [y]);
}
private def set_x_z(x: Type) {
set(x, [x], TYPE_z, arr_z);
}
private def set_xx_x(x: Type) {
set(Tuple.newType(Lists.cons2(x, x)), [x, x], x, [x]);
}
private def set_xb_x(x: Type) {
set(Tuple.newType(Lists.cons2(x, TYPE_b)), [x, TYPE_b], x, [x]);
}
private def set_xx_z(x: Type) {
set(Tuple.newType(Lists.cons2(x, x)), [x, x], TYPE_z, arr_z);
}
private def set_ftype(f: FuncType) {
sig_ = f.sig();
}
private def set_arr(p: Array<Type>, r: Array<Type>) {
sig_ = Signature.new(null, p, r);
}
private def set(p: Type, pa: Array<Type>, r: Type, ra: Array<Type>) {
sig_ = Signature.new(null, pa, ra);
}
}

def TYPE_r = AnyRef.TYPE;
def TYPE_v = Void.TYPE;
def TYPE_z = Bool.TYPE;
def TYPE_b = Byte.TYPE;
def TYPE_i = Int.TYPE;
def arr_v: Array<Type> = [];
def arr_z: Array<Type> = [TYPE_z];
def arr_zz: Array<Type> = [TYPE_z, TYPE_z];
def TYPE_zz = Tuple.fromTypeArray(arr_zz);
def TYPE_rs = V3Range.START_TYPE;
def arr_rs: Array<Type> = [TYPE_rs];
def TYPE_ab = V3.arrayByteType;

15 changes: 8 additions & 7 deletions aeneas/src/ir/IrOpMethodBuilder.v3
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class IrOpMethodBuilder(prog: Program) {
// build SSA params and types
def receiver = createGlobalIrClass();
var context = SsaContext.new(compiler, prog);
var meth = createIrMethod(receiver, typeArgs, op.sig);
var meth = createIrMethod(receiver, typeArgs, op.sig());
meth.setFact(Fact.M_INLINE);
context.enterMethod(meth);
var block = createSsa(context, receiver, meth);
Expand Down Expand Up @@ -53,26 +53,27 @@ class IrOpMethodBuilder(prog: Program) {
return ssa.addApply(null, V3Op.newCreateClosure(spec, closureType), [closure]);
}
def getClosureType(ic: IrClass, op: Operator, indexMap: Array<int>) -> Type {
var boundTypes: List<Type>;
var boundTypes: List<Type>, sig = op.sig();
for (i = indexMap.length - 1; i >= 0; i--) {
boundTypes = List.new(op.sig.paramTypes[indexMap[i]], boundTypes);
boundTypes = List.new(sig.paramTypes[indexMap[i]], boundTypes);
}
return ic.ctype.typeCon.create(boundTypes);
}
def createClosureMethod(context: SsaContext, ic: IrClass, op: Operator, abstracter: TypeParamAbstracter, indexMap: Array<int>) -> IrMethod {
// create parameters and method
op = op.subst(abstracter.substitute);
var paramTypes = Lists.toArray(op.sig.getResidualParamTypeList(indexMap));
var meth = createIrMethod(ic.ctype, abstracter.getDefaultTypeArgs(), Function.siga(paramTypes, op.sig.returnType()));
var sig = op.sig();
var paramTypes = Lists.toArray(sig.getResidualParamTypeList(indexMap));
var meth = createIrMethod(ic.ctype, abstracter.getDefaultTypeArgs(), Function.siga(paramTypes, sig.returnType()));
meth.facts |= Fact.M_INLINE;
context.enterMethod(meth);
// build the body of the method
var closureType = getClosureType(ic, op, indexMap);
var block = createSsa(context, closureType, meth);
var receiver: SsaInstr = meth.ssa.params[0];
var args = Array<SsaInstr>.new(op.sig.paramTypes.length);
var args = Array<SsaInstr>.new(sig.paramTypes.length);
var ia = 0, ip = 1;
for (i < op.sig.paramTypes.length) {
for (i < sig.paramTypes.length) {
if (ia < indexMap.length && i == indexMap[ia]) {
// the argument is bound in the closure
var spec = IrSpec.new(closureType, [closureType], ic.fields[ia]);
Expand Down
Loading
Loading