diff --git a/aeneas/src/core/Eval.v3 b/aeneas/src/core/Eval.v3 index 192f6c777..30d06d6da 100644 --- a/aeneas/src/core/Eval.v3 +++ b/aeneas/src/core/Eval.v3 @@ -813,11 +813,11 @@ def evalOp(op: Operator, args: Arguments) -> Result { } RefLayoutGetField(offset) => { var ref = args.ref(0); - return doRefLayoutGetField(args, ref, offset); + return doRefLayoutGetField(args, args.getTypeArg(1), ref, offset); } RefLayoutSetField(offset) => { var ref = args.ref(0); - return doRefLayoutSetField(args, ref, offset, args.getArg(1)); + return doRefLayoutSetField(args, args.getTypeArg(1), ref, offset, args.getArg(1)); } RefLayoutAtRepeatedField(offset, scale, max) => { var ref = args.ref(0); @@ -831,13 +831,25 @@ def evalOp(op: Operator, args: Arguments) -> Result { var ref = args.ref(0); var index = args.i(1); if (u32.view(index) >= u32.view(max)) return args.throw(V3Exception.BoundsCheck, null); - return doRefLayoutGetField(args, ref, offset + scale * index); + return doRefLayoutGetField(args, args.getTypeArg(1), ref, offset + scale * index); } RefLayoutSetRepeatedField(offset, scale, max) => { var ref = args.ref(0); var index = args.i(1); if (u32.view(index) >= u32.view(max)) return args.throw(V3Exception.BoundsCheck, null); - return doRefLayoutSetField(args, ref, offset + scale * index, args.getArg(2)); + return doRefLayoutSetField(args, args.getTypeArg(1), ref, offset + scale * index, args.getArg(2)); + } + ByteArrayGetField(offset) => { + var array = args.r(0); + var i_offset = args.i(1); + // XXX: Refactor so no intermediate ByteArrayOffset object needed + return doRefLayoutGetField(args, args.getTypeArg(0), ByteArrayOffset.new(array, offset), i_offset); + } + ByteArraySetField(offset) => { + var array = args.r(0); + var i_offset = args.i(1); + // XXX: Refactor so no intermediate ByteArrayOffset object needed + return doRefLayoutSetField(args, args.getTypeArg(0), ByteArrayOffset.new(array, offset), i_offset, args.getArg(2)); } //---------------------------------------------------------------------------- @@ -864,10 +876,9 @@ def evalOp(op: Operator, args: Arguments) -> Result { return args.throw("EvalUnimplemented", op.opcode.name); } -def doRefLayoutGetField(args: Arguments, ref: ByteArrayOffset, offset: int) -> Result { +def doRefLayoutGetField(args: Arguments, fieldType: Type, ref: ByteArrayOffset, offset: int) -> Result { if (ref == null || ref.array == null) return args.throw(V3Exception.NullCheck, null); - var t = args.getTypeArg(1); - match (t) { + match (fieldType) { x: IntType => { var width = (x.width + 7) & ~7; // round up bitwidth var byteSize = width >> 3; @@ -891,14 +902,13 @@ def doRefLayoutGetField(args: Arguments, ref: ByteArrayOffset, offset: int) -> R if (x.is64) return Float64Val.new(v); else return Float32Val.new(u32.view(v)); } - _ => return args.throw("EvalException", Strings.format1("invalid RefLayoutField type %q", t.render)); + _ => return args.throw("EvalException", Strings.format1("invalid RefLayoutField type %q", fieldType.render)); } } -def doRefLayoutSetField(args: Arguments, ref: ByteArrayOffset, offset: int, val: Val) -> Result { +def doRefLayoutSetField(args: Arguments, fieldType: Type, ref: ByteArrayOffset, offset: int, val: Val) -> Result { if (ref == null || ref.array == null) return args.throw(V3Exception.NullCheck, null); - var t = args.getTypeArg(1); var size = 0, bits: u64 = 0; - match (t) { + match (fieldType) { x: IntType => { size = x.packedByteSize(); bits = if(x.width <= 32, u32.view(Int.unbox(val)), u64.view(Long.unboxSU(val, x.signed))); @@ -913,7 +923,7 @@ def doRefLayoutSetField(args: Arguments, ref: ByteArrayOffset, offset: int, val: else if (Float64Val.?(val)) bits = Float64Val.!(val).bits; } // TODO: enum types? - _ => return args.throw("EvalException", Strings.format1("invalid RefLayoutField type %q", t.render)); + _ => return args.throw("EvalException", Strings.format1("invalid RefLayoutField type %q", fieldType.render)); } ref.write(false, offset, size, bits); return Values.BOTTOM; diff --git a/aeneas/src/core/Opcode.v3 b/aeneas/src/core/Opcode.v3 index 07b64044e..57896ec3c 100644 --- a/aeneas/src/core/Opcode.v3 +++ b/aeneas/src/core/Opcode.v3 @@ -125,6 +125,8 @@ type Opcode { case RefLayoutAtRepeatedField(offset: int, scale: int, max: int); case RefLayoutGetRepeatedField(offset: int, scale: int, max: int); case RefLayoutSetRepeatedField(offset: int, scale: int, max: int); + case ByteArrayGetField(offset: int); + case ByteArraySetField(offset: int); // System operations case SystemCall(syscall: SystemCall); // Container for VST operations diff --git a/aeneas/src/core/Operator.v3 b/aeneas/src/core/Operator.v3 index 16eb23fbb..6ce4c145f 100644 --- a/aeneas/src/core/Operator.v3 +++ b/aeneas/src/core/Operator.v3 @@ -517,6 +517,14 @@ component V3Op { var opcode = Opcode.RefLayoutSetRepeatedField(offset, scale, max); return newOp0(opcode, [refType, fieldType], [refType, Int.TYPE, fieldType], Void.TYPE); } + def newByteArrayGetField(offset: int, fieldType: Type) -> Operator { + var opcode = Opcode.ByteArrayGetField(offset); + return newOp0(opcode, [fieldType], [V3.arrayByteType, Int.TYPE], fieldType); + } + def newByteArraySetField(offset: int, fieldType: Type) -> Operator { + var opcode = Opcode.ByteArraySetField(offset); + return newOp0(opcode, [fieldType], [V3.arrayByteType, Int.TYPE, fieldType], Void.TYPE); + } //---------------------------------------------------------------------------- def bestCallVirtual(spec: IrSpec) -> Operator { if (spec.receiver.typeCon.kind == V3Kind.CLASS) { @@ -594,6 +602,8 @@ def renderOp(op: Operator, buf: StringBuilder) -> StringBuilder { RefLayoutIn(offset) => rfunc = StringBuilder.putd(_, offset); RefLayoutGetField(offset) => rfunc = StringBuilder.putd(_, offset); RefLayoutSetField(offset) => rfunc = StringBuilder.putd(_, offset); + ByteArrayGetField(offset) => rfunc = StringBuilder.putd(_, offset); + ByteArraySetField(offset) => rfunc = StringBuilder.putd(_, offset); ConditionalThrow(exception) => rfunc = StringBuilder.puts(_, exception); SystemCall(syscall) => rfunc = StringBuilder.puts(_, syscall.name); VstSugar(op) => rfunc = StringBuilder.puts(_, op.name); diff --git a/aeneas/src/ir/SsaNormalizer.v3 b/aeneas/src/ir/SsaNormalizer.v3 index 2281daf3c..755874153 100644 --- a/aeneas/src/ir/SsaNormalizer.v3 +++ b/aeneas/src/ir/SsaNormalizer.v3 @@ -369,60 +369,47 @@ class SsaRaNormalizer extends SsaRebuilder { var fn = normTypeArg(op, 1); var newArgs = genRefs(app.inputs); var array = newArgs[0], i_offset = newArgs[an.size]; + var facts: Fact.set = Fact.O_NO_BOUNDS_CHECK; + var result: SsaInstr; + match (fn.oldType) { - x: IntType => { - var result = genArrayByteGetMultiple(app, x, array, i_offset, offset); - var wt = Int.getType(false, x.byteSize() * 8); - result = curBlock.opIntViewI0(wt, x, result); - return map1(app, result); - } + x: IntType => result = curBlock.opByteArrayGetField(x, offset, facts, array, i_offset); + x: FloatType => result = curBlock.opByteArrayGetField(x, offset, facts, array, i_offset); x: EnumType => { var it = IntType.!(x.enumDecl.tagType); - var result = genArrayByteGetMultiple(app, it, array, i_offset, offset); + var wt = Int.getType(false, it.byteSize() * 8); + result = curBlock.opByteArrayGetField(wt, offset, facts, array, i_offset); var caseCount = newGraph.intConst(x.enumDecl.cases.length); var inBound = curBlock.opIntULt(norm.config.ArrayLengthType, it, result, caseCount); - var wt = Int.getType(false, it.byteSize() * 8); - result = curBlock.opIntViewI0(wt, it, result); + result = curBlock.opIntViewI0(it, wt, result); var split = SsaBlockSplit.new(context, curBlock); curBlock = split.addIf(inBound); curBlock = split.addElse(); curBlock = split.finish(); curBlock.opIntViewI0(wt, it, result); result = split.addPhi(it, [result, newGraph.nullConst(it)]); - return map1(app, result); - } - x: FloatType => { - var it = Int.getType(false, x.total_width); - var result = genArrayByteGetMultiple(app, it, array, i_offset, offset); - var wt = Int.getType(false, it.byteSize() * 8); - result = curBlock.opIntViewI0(wt, it, result); - var floatViewOp = if(x.is64, V3Op.newFloat64ViewI(it), V3Op.newFloat32ViewI(it)); - result = curBlock.addApply(app.source, floatViewOp, [result]); - return map1(app, result); } _ => context.fail1("unexpected type %q", fn.oldType.render); } + return map1(app, result); } RefLayoutSetField(offset) => { var fn = normTypeArg(op, 1); var newArgs = genRefs(args); var array = newArgs[0], i_offset = newArgs[1], val = newArgs[2]; - match(fn.oldType) { - x: IntType => { - genArrayByteSetMultiple(app, x, array, i_offset, offset, val); - } + var facts: Fact.set = Fact.O_NO_BOUNDS_CHECK; + var result: SsaInstr; + + match (fn.oldType) { + x: IntType => result = curBlock.opByteArraySetField(fn.oldType, offset, facts, array, i_offset, val); + x: FloatType => result = curBlock.opByteArraySetField(fn.oldType, offset, facts, array, i_offset, val); x: EnumType => { var it = IntType.!(x.enumDecl.tagType); - genArrayByteSetMultiple(app, it, array, i_offset, offset, val); - } - x: FloatType => { - var it = Int.getType(false, x.total_width); - var width = x.byteSize(); - val = curBlock.opIntView(x, it, val); - genArrayByteSetMultiple(app, it, array, i_offset, offset, val); + var wt = Int.getType(false, it.byteSize() * 8); + result = curBlock.opByteArraySetField(wt, offset, facts, array, i_offset, val); } - _ => context.fail1("unexpected type %q", fn.oldType.render); } + return map1(app, result); } RefLayoutGetRepeatedField(offset, scale, max) => { var an = arrayByteNorm; @@ -436,38 +423,24 @@ class SsaRaNormalizer extends SsaRebuilder { } var result = SsaInstr.!(newGraph.intConst(0)); i_offset = curBlock.opIntAdd(curBlock.opIntMul(index, newGraph.intConst(scale)), i_offset); + var facts: Fact.set = Fact.O_NO_BOUNDS_CHECK; match (fn.oldType) { - x: IntType => { - var result = genArrayByteGetMultiple(app, x, array, i_offset, offset); - var wt = Int.getType(false, x.byteSize() * 8); - result = curBlock.opIntViewI0(wt, x, result); - return map1(app, result); - } + x: IntType => result = curBlock.opByteArrayGetField(x, offset, facts, array, i_offset); + x: FloatType => result = curBlock.opByteArrayGetField(x, offset, facts, array, i_offset); x: EnumType => { var it = IntType.!(x.enumDecl.tagType); - var result = genArrayByteGetMultiple(app, it, array, i_offset, offset); + result = curBlock.opByteArrayGetField(it, offset, facts, array, i_offset); var caseCount = newGraph.intConst(x.enumDecl.cases.length); var inBound = curBlock.opIntULt(it, it, result, caseCount); var split = SsaBlockSplit.new(context, curBlock); curBlock = split.addIf(inBound); curBlock = split.addElse(); curBlock = split.finish(); - var wt = Int.getType(false, it.byteSize() * 8); - result = curBlock.opIntViewI0(wt, it, result); result = split.addPhi(it, [result, newGraph.nullConst(it)]); - return map1(app, result); - } - x: FloatType => { - var it = Int.getType(false, x.total_width); - var result = genArrayByteGetMultiple(app, it, array, i_offset, offset); - var wt = Int.getType(false, it.byteSize() * 8); - result = curBlock.opIntViewI0(wt, it, result); - var floatViewOp = if(x.is64, V3Op.newFloat64ViewI(it), V3Op.newFloat32ViewI(it)); - result = curBlock.addApply(app.source, floatViewOp, [result]); - return map1(app, result); } _ => ; } + return map1(app, result); } RefLayoutSetRepeatedField(offset, scale, max) => { var fn = normTypeArg(op, 1); @@ -479,22 +452,17 @@ class SsaRaNormalizer extends SsaRebuilder { curBlock.opConditionalThrow(V3Exception.BoundsCheck, oob); } i_offset = curBlock.opIntAdd(curBlock.opIntMul(index, newGraph.intConst(scale)), i_offset); - match(val.getType()) { - x: IntType => { - genArrayByteSetMultiple(app, x, array, i_offset, offset, val); - } + var facts: Fact.set = Fact.O_NO_BOUNDS_CHECK; + var result: SsaInstr; + match (fn.oldType) { + x: IntType => result = curBlock.opByteArraySetField(fn.oldType, offset, facts, array, i_offset, val); + x: FloatType => result = curBlock.opByteArraySetField(fn.oldType, offset, facts, array, i_offset, val); x: EnumType => { var it = IntType.!(x.enumDecl.tagType); - genArrayByteSetMultiple(app, it, array, i_offset, offset, val); + result = curBlock.opByteArraySetField(it, offset, facts, array, i_offset, val); } - x: FloatType => { - var it = Int.getType(false, x.total_width); - var op = if(x.is64, V3Op.opIntViewF64, V3Op.opIntViewF32); - val = curBlock.pure(op, [val]); - genArrayByteSetMultiple(app, it, array, i_offset, offset, val); - } - _ => ; } + return map1(app, result); } PtrAtContents => { // rewrite to PtrAtArrayElem diff --git a/aeneas/src/jvm/JvmType.v3 b/aeneas/src/jvm/JvmType.v3 index 7fef744e7..2c3403872 100644 --- a/aeneas/src/jvm/JvmType.v3 +++ b/aeneas/src/jvm/JvmType.v3 @@ -199,4 +199,16 @@ component JvmTypes { def SIG_VOID_BYTE_ARRAY = JvmSig.new([], JvmTypes.BYTE_ARRAY); def SIG_DOUBLE_DOUBLE_DOUBLE_LONG = JvmSig.new([DOUBLE, DOUBLE, DOUBLE], LONG); def SIG_DOUBLE_DOUBLE_DOUBLE_BOOLEAN = JvmSig.new([DOUBLE, DOUBLE, DOUBLE], BOOLEAN); + def SIG_BYTE_ARRAY_INT_INT_BYTE = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, INT], BYTE); + def SIG_BYTE_ARRAY_INT_INT_SHORT = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, INT], SHORT); + def SIG_BYTE_ARRAY_INT_INT_INT = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, INT], INT); + def SIG_BYTE_ARRAY_INT_INT_LONG = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, INT], LONG); + def SIG_BYTE_ARRAY_INT_INT_FLOAT = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, INT], FLOAT); + def SIG_BYTE_ARRAY_INT_INT_DOUBLE = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, INT], DOUBLE); + def SIG_BYTE_ARRAY_INT_BYTE_INT_VOID = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, BYTE, INT], VOID); + def SIG_BYTE_ARRAY_INT_SHORT_INT_VOID = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, SHORT, INT], VOID); + def SIG_BYTE_ARRAY_INT_INT_INT_VOID = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, INT, INT], VOID); + def SIG_BYTE_ARRAY_INT_LONG_INT_VOID = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, LONG, INT], VOID); + def SIG_BYTE_ARRAY_INT_FLOAT_INT_VOID = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, FLOAT, INT], VOID); + def SIG_BYTE_ARRAY_INT_DOUBLE_INT_VOID = JvmSig.new([JvmTypes.BYTE_ARRAY, INT, DOUBLE, INT], VOID); } diff --git a/aeneas/src/jvm/SsaJvmGen.v3 b/aeneas/src/jvm/SsaJvmGen.v3 index 1f5103cf6..38f976192 100644 --- a/aeneas/src/jvm/SsaJvmGen.v3 +++ b/aeneas/src/jvm/SsaJvmGen.v3 @@ -574,6 +574,51 @@ class SsaJvmGen(jprog: JvmProgram, context: SsaContext, jsig: JvmSig, code: JvmC emitThrow(exception); code.patchBranch(b); } + ByteArrayGetField(offset) => { + var fieldType = op.typeArgs[0]; + code.iconst(offset); + match (fieldType) { + x: IntType => { + match (x.packedByteSize()) { + 1 => jprog.invokesystem(code, "readBytes1", JvmTypes.SIG_BYTE_ARRAY_INT_INT_BYTE); + 2 => jprog.invokesystem(code, "readBytes2", JvmTypes.SIG_BYTE_ARRAY_INT_INT_SHORT); + 3 => jprog.invokesystem(code, "readBytes3", JvmTypes.SIG_BYTE_ARRAY_INT_INT_INT); + 4 => jprog.invokesystem(code, "readBytes4", JvmTypes.SIG_BYTE_ARRAY_INT_INT_INT); + 5 => jprog.invokesystem(code, "readBytes5", JvmTypes.SIG_BYTE_ARRAY_INT_INT_LONG); + 6 => jprog.invokesystem(code, "readBytes6", JvmTypes.SIG_BYTE_ARRAY_INT_INT_LONG); + 7 => jprog.invokesystem(code, "readBytes7", JvmTypes.SIG_BYTE_ARRAY_INT_INT_LONG); + 8 => jprog.invokesystem(code, "readBytes8", JvmTypes.SIG_BYTE_ARRAY_INT_INT_LONG); + } + emitIntTrunc(x); + } + x: FloatType => { + if (x == Float.FLOAT32) jprog.invokesystem(code, "readBytesFloat", JvmTypes.SIG_BYTE_ARRAY_INT_INT_FLOAT); + else jprog.invokesystem(code, "readBytesDouble", JvmTypes.SIG_BYTE_ARRAY_INT_INT_DOUBLE); + } + } + } + ByteArraySetField(offset) => { + var fieldType = op.typeArgs[0]; + code.iconst(offset); + match (fieldType) { + x: IntType => { + match (x.packedByteSize()) { + 1 => jprog.invokesystem(code, "setBytes1", JvmTypes.SIG_BYTE_ARRAY_INT_BYTE_INT_VOID); + 2 => jprog.invokesystem(code, "setBytes2", JvmTypes.SIG_BYTE_ARRAY_INT_SHORT_INT_VOID); + 3 => jprog.invokesystem(code, "setBytes3", JvmTypes.SIG_BYTE_ARRAY_INT_INT_INT_VOID); + 4 => jprog.invokesystem(code, "setBytes4", JvmTypes.SIG_BYTE_ARRAY_INT_INT_INT_VOID); + 5 => jprog.invokesystem(code, "setBytes5", JvmTypes.SIG_BYTE_ARRAY_INT_LONG_INT_VOID); + 6 => jprog.invokesystem(code, "setBytes6", JvmTypes.SIG_BYTE_ARRAY_INT_LONG_INT_VOID); + 7 => jprog.invokesystem(code, "setBytes7", JvmTypes.SIG_BYTE_ARRAY_INT_LONG_INT_VOID); + 8 => jprog.invokesystem(code, "setBytes8", JvmTypes.SIG_BYTE_ARRAY_INT_LONG_INT_VOID); + } + } + x: FloatType => { + if (x == Float.FLOAT32) jprog.invokesystem(code, "setBytesFloat", JvmTypes.SIG_BYTE_ARRAY_INT_FLOAT_INT_VOID); + else jprog.invokesystem(code, "setBytesDouble", JvmTypes.SIG_BYTE_ARRAY_INT_DOUBLE_INT_VOID); + } + } + } } else { context.fail1("unexpected opcode in SSA->JVM: %s", op.opcode.name); } diff --git a/aeneas/src/mach/MachLowering.v3 b/aeneas/src/mach/MachLowering.v3 index 214ec3c5b..91e5a43d5 100644 --- a/aeneas/src/mach/MachLowering.v3 +++ b/aeneas/src/mach/MachLowering.v3 @@ -493,6 +493,8 @@ class MachLowering(mach: MachProgram, compiler: Compiler, config: MachLoweringCo ArrayTupleInit(elems, length) => ni = genArrayTupleInit(oi, elems, length); ArrayGetElem => return genArrayGetElem(oi, 0); ArraySetElem => return genArraySetElem(oi, 0); + ByteArrayGetField(offset) => return genByteArrayGetField(oi, offset); + ByteArraySetField(offset) => return genByteArraySetField(oi, offset); ArrayGetElemElem(index) => return genArrayGetElem(oi, index); ArraySetElemElem(index) => return genArraySetElem(oi, index); ArrayGetLength => ni = genArrayGetLength(oi); @@ -1450,6 +1452,33 @@ class MachLowering(mach: MachProgram, compiler: Compiler, config: MachLoweringCo oi.kill(); oi.remove(); } + def genByteArrayGetField(oi: SsaApplyOp, offset: int) { + var inputs = normRefs(oi.inputs); + var narr = inputs[0], ni_offset = inputs[1]; + var fieldType = oi.op.typeArgs[0]; + var nullity = context.compiler.nullity(oi, narr); + nullity = genBoundsCheck(oi, nullity).1; + var arrayRep = mach.arrayRep(V3.arrayByteType); + var arrayOffset = ptrAdd(narr, context.graph.intConst(offset)); + var arrayElemOffset = genArrayElemOffset(arrayRep.getElemElemOffset(0), 1, ni_offset); + var machType = mach.machType(fieldType); + var loads = genNormTypedLoadsByteRegion(oi.source, machType, ptrAdd(arrayOffset, arrayElemOffset), 0); + mapN(oi, loads); + } + def genByteArraySetField(oi: SsaApplyOp, offset: int) { + var inputs = normRefs(oi.inputs); + var narr = inputs[0], ni_offset = inputs[1], nvalue = inputs[2]; + var fieldType = oi.op.typeArgs[0]; + var nullity = context.compiler.nullity(oi, narr); + nullity = genBoundsCheck(oi, nullity).1; + var arrayRep = mach.arrayRep(V3.arrayByteType); + var arrayOffset = ptrAdd(narr, context.graph.intConst(offset)); + var arrayElemOffset = genArrayElemOffset(arrayRep.getElemElemOffset(0), 1, ni_offset); + var machType = mach.machType(fieldType); + genNormTypedStoresByteRegion(oi, machType, ptrAdd(arrayOffset, arrayElemOffset), 0, inputs, 2); + oi.kill(); + oi.remove(); + } def genArrayElemOffset(headerSize: int, scale: int, index: SsaInstr) -> SsaInstr { if (SsaConst.?(index)) { // fold the offset calculation @@ -1862,6 +1891,37 @@ class MachLowering(mach: MachProgram, compiler: Compiler, config: MachLoweringCo } return loads; } + def genNormTypedLoadsByteRegion(source: Source, machType: Type, base: SsaInstr, offset: int) -> Array { + var tn: TypeNorm; + match (machType) { + x: IntType => tn = mach.intNorm.makeType(x); + _ => return [ptrLoad(machType, base, offset)]; + } + + var loads = Array.new(tn.size); + for (i < tn.size - 1) { + var et = tn.sub[i]; + loads[i] = ptrLoad(et, base, offset); + offset = offset + mach.sizeOf(et); + } + + var et = IntType.!(tn.sub[tn.size - 1]); + var etWidth = et.packedByteSize() * 8, read = 0; + for (curWidth = mach.intNorm.width; curWidth > 0; curWidth /= 2) { + if ((etWidth & curWidth) != 0) { + var tempLoad = ptrLoad(Int.getType(false, curWidth), base, offset + read / 8); + if (read != 0) { + tempLoad = apply(source, et.opShl(), [tempLoad, context.graph.intConst(read)]); + tempLoad = apply(source, et.opOr(), [loads[tn.size - 1], tempLoad]); + } + loads[tn.size - 1] = tempLoad; + read += curWidth; + } + } + loads[tn.size - 1] = curBlock.opIntViewI0(Int.getType(false, etWidth), et, loads[tn.size - 1]); + + return loads; + } def genNormTypedStores(oi: SsaApplyOp, nullity: Fact.set, init: bool, machType: Type, base: SsaInstr, offset: int, vals: Array, start: int) { var tn = normIntType(machType), nullCheck = !nullity.O_NO_NULL_CHECK; // check for simple case first. @@ -1880,6 +1940,34 @@ class MachLowering(mach: MachProgram, compiler: Compiler, config: MachLoweringCo offset = offset + mach.sizeOf(et); } } + def genNormTypedStoresByteRegion(oi: SsaApplyOp, machType: Type, base: SsaInstr, offset: int, vals: Array, start: int) { + var tn: TypeNorm; + match (machType) { + x: IntType => tn = mach.intNorm.makeType(x); + _ => { + ptrStore(machType, base, offset, vals[start]); + return; + } + } + + for (i < tn.size - 1) { + var et = tn.sub[i]; + ptrStore(et, base, offset, vals[start + i]); + offset = offset + mach.sizeOf(et); + } + + var et = IntType.!(tn.sub[tn.size - 1]), val = vals[start + tn.size - 1]; + var etWidth = et.packedByteSize() * 8; + var stored = 0, curStore: SsaInstr = null; + for (curWidth = mach.intNorm.width; curWidth > 0; curWidth /= 2) { + if ((etWidth & curWidth) != 0) { + var valTruncated = curBlock.opIntViewI0(et, Int.getType(false, curWidth), val); + val = apply(oi.source, et.opShl(), [val, context.graph.intConst(curWidth)]); + ptrStore(Int.getType(false, curWidth), base, offset + stored / 8, valTruncated); + stored += curWidth; + } + } + } def isNonTrivialStore(init: bool, v: SsaInstr) -> bool { if (init) { if (SsaConst.?(v)) return !Values.equal(SsaConst.!(v).val, null); diff --git a/aeneas/src/ssa/SsaBuilder.v3 b/aeneas/src/ssa/SsaBuilder.v3 index 1f2a67756..1673d4290 100644 --- a/aeneas/src/ssa/SsaBuilder.v3 +++ b/aeneas/src/ssa/SsaBuilder.v3 @@ -517,6 +517,12 @@ class SsaBuilder extends SsaBlockState { if (!facts.O_NO_BOUNDS_CHECK) facts |= tryElimBoundsCheck(x, y); return add(V3Op.newArraySetElem(arrayType, indexType), [x, y, z], facts); } + def opByteArrayGetField(fieldType: Type, offset: int, facts: Fact.set, x: SsaInstr, y: SsaInstr) -> SsaInstr { + return add(V3Op.newByteArrayGetField(offset, fieldType), [x, y], facts); + } + def opByteArraySetField(fieldType: Type, offset: int, facts: Fact.set, x: SsaInstr, y: SsaInstr, z: SsaInstr) -> SsaInstr { + return add(V3Op.newByteArraySetField(offset, fieldType), [x, y, z], facts); + } private def tryElimBoundsCheck(x: SsaInstr, y: SsaInstr) -> Fact.set { if (SsaConst.?(y)) { var yk = SsaConst.!(y).val; diff --git a/bin/dev/aeneas b/bin/dev/aeneas index 1f3234543..211d957de 100755 --- a/bin/dev/aeneas +++ b/bin/dev/aeneas @@ -357,7 +357,7 @@ function run_bump() { function revert_to_stable() { do_clean $BIN/.setup-v3c - readlink $V3C_LINK + readlink -f $V3C_LINK } release_help="Release the current Aeneas binaries, overwriting existing stable binaries" diff --git a/rt/jvm/bin/V3S_System.class b/rt/jvm/bin/V3S_System.class index 73db170e2..443828c72 100644 Binary files a/rt/jvm/bin/V3S_System.class and b/rt/jvm/bin/V3S_System.class differ diff --git a/rt/jvm/src/V3S_System.java b/rt/jvm/src/V3S_System.java index 620fbd638..83d0e3b58 100644 --- a/rt/jvm/src/V3S_System.java +++ b/rt/jvm/src/V3S_System.java @@ -429,4 +429,165 @@ public static boolean feq(float a, float b) { public static boolean deq(double a, double b) { return Double.doubleToRawLongBits(a) == Double.doubleToRawLongBits(b); } + + public static byte readBytes1(byte[] bytes, int offset, int staticOffset) { + return bytes[offset + staticOffset]; + } + + public static short readBytes2(byte[] bytes, int offset, int staticOffset) { + int fullOffset = offset + staticOffset; + int byte0 = ((int)bytes[fullOffset] & 0xFF); + int byte1 = ((int)bytes[fullOffset + 1] & 0xFF) << 8; + return ((short)(byte0 | byte1)); + } + + public static int readBytes3(byte[] bytes, int offset, int staticOffset) { + int fullOffset = offset + staticOffset; + int byte0 = ((int)bytes[fullOffset] & 0xFF); + int byte1 = ((int)bytes[fullOffset + 1] & 0xFF) << 8; + int byte2 = ((int)bytes[fullOffset + 2] & 0xFF) << 16; + return byte0 | byte1 | byte2; + } + + public static int readBytes4(byte[] bytes, int offset, int staticOffset) { + int fullOffset = offset + staticOffset; + int byte0 = ((int)bytes[fullOffset] & 0xFF); + int byte1 = ((int)bytes[fullOffset + 1] & 0xFF) << 8; + int byte2 = ((int)bytes[fullOffset + 2] & 0xFF) << 16; + int byte3 = ((int)bytes[fullOffset + 3] & 0xFF) << 24; + return byte0 | byte1 | byte2 | byte3; + } + + public static long readBytes5(byte[] bytes, int offset, int staticOffset) { + int fullOffset = offset + staticOffset; + long byte0 = ((long)bytes[fullOffset] & 0xFF); + long byte1 = ((long)bytes[fullOffset + 1] & 0xFF) << 8; + long byte2 = ((long)bytes[fullOffset + 2] & 0xFF) << 16; + long byte3 = ((long)bytes[fullOffset + 3] & 0xFF) << 24; + long byte4 = ((long)bytes[fullOffset + 4] & 0xFF) << 32; + return byte0 | byte1 | byte2 | byte3 | byte4; + } + + public static long readBytes6(byte[] bytes, int offset, int staticOffset) { + int fullOffset = offset + staticOffset; + long byte0 = ((long)bytes[fullOffset] & 0xFF); + long byte1 = ((long)bytes[fullOffset + 1] & 0xFF) << 8; + long byte2 = ((long)bytes[fullOffset + 2] & 0xFF) << 16; + long byte3 = ((long)bytes[fullOffset + 3] & 0xFF) << 24; + long byte4 = ((long)bytes[fullOffset + 4] & 0xFF) << 32; + long byte5 = ((long)bytes[fullOffset + 5] & 0xFF) << 40; + return byte0 | byte1 | byte2 | byte3 | byte4 | byte5; + } + + public static long readBytes7(byte[] bytes, int offset, int staticOffset) { + int fullOffset = offset + staticOffset; + long byte0 = ((long)bytes[fullOffset] & 0xFF); + long byte1 = ((long)bytes[fullOffset + 1] & 0xFF) << 8; + long byte2 = ((long)bytes[fullOffset + 2] & 0xFF) << 16; + long byte3 = ((long)bytes[fullOffset + 3] & 0xFF) << 24; + long byte4 = ((long)bytes[fullOffset + 4] & 0xFF) << 32; + long byte5 = ((long)bytes[fullOffset + 5] & 0xFF) << 40; + long byte6 = ((long)bytes[fullOffset + 6] & 0xFF) << 48; + return byte0 | byte1 | byte2 | byte3 | byte4 | byte5 | byte6; + } + + public static long readBytes8(byte[] bytes, int offset, int staticOffset) { + int fullOffset = offset + staticOffset; + long byte0 = ((long)bytes[fullOffset] & 0xFF); + long byte1 = ((long)bytes[fullOffset + 1] & 0xFF) << 8; + long byte2 = ((long)bytes[fullOffset + 2] & 0xFF) << 16; + long byte3 = ((long)bytes[fullOffset + 3] & 0xFF) << 24; + long byte4 = ((long)bytes[fullOffset + 4] & 0xFF) << 32; + long byte5 = ((long)bytes[fullOffset + 5] & 0xFF) << 40; + long byte6 = ((long)bytes[fullOffset + 6] & 0xFF) << 48; + long byte7 = ((long)bytes[fullOffset + 7] & 0xFF) << 56; + return byte0 | byte1 | byte2 | byte3 | byte4 | byte5 | byte6 | byte7; + } + + public static float readBytesFloat(byte[] bytes, int offset, int staticOffset) { + int bits = V3S_System.readBytes4(bytes, offset, staticOffset); + return Float.intBitsToFloat(bits); + } + + public static double readBytesDouble(byte[] bytes, int offset, int staticOffset) { + long bits = V3S_System.readBytes8(bytes, offset, staticOffset); + return Double.longBitsToDouble(bits); + } + + public static void setBytes1(byte[] bytes, int offset, byte value, int staticOffset) { + bytes[offset + staticOffset] = value; + } + + public static void setBytes2(byte[] bytes, int offset, short value, int staticOffset) { + int fullOffset = offset + staticOffset; + bytes[fullOffset] = (byte)value; + bytes[fullOffset + 1] = (byte)(value >> 8); + } + + public static void setBytes3(byte[] bytes, int offset, int value, int staticOffset) { + int fullOffset = offset + staticOffset; + bytes[fullOffset] = (byte)value; + bytes[fullOffset + 1] = (byte)(value >> 8); + bytes[fullOffset + 2] = (byte)(value >> 16); + } + + public static void setBytes4(byte[] bytes, int offset, int value, int staticOffset) { + int fullOffset = offset + staticOffset; + bytes[fullOffset] = (byte)value; + bytes[fullOffset + 1] = (byte)(value >> 8); + bytes[fullOffset + 2] = (byte)(value >> 16); + bytes[fullOffset + 3] = (byte)(value >> 24); + } + + public static void setBytes5(byte[] bytes, int offset, long value, int staticOffset) { + int fullOffset = offset + staticOffset; + bytes[fullOffset] = (byte)value; + bytes[fullOffset + 1] = (byte)(value >> 8); + bytes[fullOffset + 2] = (byte)(value >> 16); + bytes[fullOffset + 3] = (byte)(value >> 24); + bytes[fullOffset + 4] = (byte)(value >> 32); + } + + public static void setBytes6(byte[] bytes, int offset, long value, int staticOffset) { + int fullOffset = offset + staticOffset; + bytes[fullOffset] = (byte)value; + bytes[fullOffset + 1] = (byte)(value >> 8); + bytes[fullOffset + 2] = (byte)(value >> 16); + bytes[fullOffset + 3] = (byte)(value >> 24); + bytes[fullOffset + 4] = (byte)(value >> 32); + bytes[fullOffset + 5] = (byte)(value >> 40); + } + + public static void setBytes7(byte[] bytes, int offset, long value, int staticOffset) { + int fullOffset = offset + staticOffset; + bytes[fullOffset] = (byte)value; + bytes[fullOffset + 1] = (byte)(value >> 8); + bytes[fullOffset + 2] = (byte)(value >> 16); + bytes[fullOffset + 3] = (byte)(value >> 24); + bytes[fullOffset + 4] = (byte)(value >> 32); + bytes[fullOffset + 5] = (byte)(value >> 40); + bytes[fullOffset + 6] = (byte)(value >> 48); + } + + public static void setBytes8(byte[] bytes, int offset, long value, int staticOffset) { + int fullOffset = offset + staticOffset; + bytes[fullOffset] = (byte)value; + bytes[fullOffset + 1] = (byte)(value >> 8); + bytes[fullOffset + 2] = (byte)(value >> 16); + bytes[fullOffset + 3] = (byte)(value >> 24); + bytes[fullOffset + 4] = (byte)(value >> 32); + bytes[fullOffset + 5] = (byte)(value >> 40); + bytes[fullOffset + 6] = (byte)(value >> 48); + bytes[fullOffset + 7] = (byte)(value >> 56); + } + + public static void setBytesFloat(byte[] bytes, int offset, float value, int staticOffset) { + int bits = Float.floatToRawIntBits(value); + V3S_System.setBytes4(bytes, offset, bits, staticOffset); + } + + public static void setBytesDouble(byte[] bytes, int offset, double value, int staticOffset) { + long bits = Double.doubleToRawLongBits(value); + V3S_System.setBytes8(bytes, offset, bits, staticOffset); + } } diff --git a/test/bin/src/V3S_Tester.java b/test/bin/src/V3S_Tester.java index 6c703eee4..09109c731 100644 --- a/test/bin/src/V3S_Tester.java +++ b/test/bin/src/V3S_Tester.java @@ -107,7 +107,7 @@ private static boolean compare(Object o1, Object o2) { if (o2 instanceof Boolean) return o2.equals(o1); if (isIntegral(o1) && isIntegral(o2)) { int mask = maskOf(o1) & maskOf(o2); - return (intValue(o1) & mask) == (intValue(o2) & mask); + return (intValue(o1) & mask) == (intValue(o2) & mask); } return o1.equals(o2); } diff --git a/test/layout/write_u24a.v3 b/test/layout/write_u24a.v3 new file mode 100644 index 000000000..b8662dbce --- /dev/null +++ b/test/layout/write_u24a.v3 @@ -0,0 +1,19 @@ +//@execute -1=!BoundsCheckException; 0=88; 1=88; 2=88; 3=!BoundsCheckException +layout S { + +0 field: u24; + =3; +} + +def bytes: Array = [ 12, 23, 34, 45, 56 ]; +def oldBytes: Array = [ 12, 23, 34, 45, 56 ]; + +def main(a: int) -> u24 { + var r = Ref.at(bytes, a); + r.field = 88; + + if (a < bytes.length - 3 && bytes[a+3] != oldBytes[a+3]) { + // check bytes were not overwritten + return 99; + } + return r.field; +} diff --git a/test/layout/write_u56a.v3 b/test/layout/write_u56a.v3 new file mode 100644 index 000000000..003422101 --- /dev/null +++ b/test/layout/write_u56a.v3 @@ -0,0 +1,19 @@ +//@execute -1=!BoundsCheckException; 0=88; 1=88; 2=88; 3=!BoundsCheckException +layout S { + +0 field: u56; + =7; +} + +def bytes: Array = [ 12, 23, 34, 45, 56, 45, 34, 23, 12 ]; +def oldBytes: Array = [ 12, 23, 34, 45, 56, 45, 34, 23, 12 ]; + +def main(a: int) -> int { + var r = Ref.at(bytes, a); + r.field = 88; + + if (a < bytes.length - 7 && bytes[a+7] != oldBytes[a+7]) { + // check bytes were not overwritten + return 99; + } + return int.!(r.field); +} \ No newline at end of file