From a7cffed839eea8003582e530116f0e3668046e54 Mon Sep 17 00:00:00 2001 From: David Thien Date: Tue, 18 Oct 2022 12:05:41 -0700 Subject: [PATCH 1/2] Added Guard ops for fixed and dynamic slot values --- notes/cacheir.cachet | 58 ++++++++++++++++++++++++++++++++++++++++++++ notes/js.cachet | 8 ++++++ notes/masm.cachet | 20 +++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/notes/cacheir.cachet b/notes/cacheir.cachet index 6a329de..61c49cf 100644 --- a/notes/cacheir.cachet +++ b/notes/cacheir.cachet @@ -65,6 +65,7 @@ impl OperandLocation { struct OperandId; +// Corresponds to a ValOperandId struct ValueId <: OperandId; impl ValueId { @@ -467,6 +468,53 @@ ir CacheIR emits MASM { CacheIR::releaseReg(scratchReg); } + op GuardFixedSlotValue(objId: ObjectId, + offsetOffset: Int32Field, + valOffset: ValueField) { + let obj = CacheIR::useObjectReg(objId); + let offset = CacheIR::allocateReg(); + let scratchVal = CacheIR::allocateValueReg(); + + CacheIR::addFailurePath(out label failure); + + CacheIR::emitLoadInt32StubField(offsetOffset, offset); + CacheIR::emitLoadValueStubField(valOffset, scratchVal); + + let slotVal = BaseIndex::new(obj, offset, Scale::TimesOne, 0_u32); + emit MASM::BranchTestValue( + Condition::NotEqual, slotVal, scratchVal, failure + ); + + CacheIR::releaseReg(offset); + CacheIR::releaseValueReg(scratchVal); + } + + op GuardDynamicSlotValue(objId: ObjectId, + offsetOffset: Int32Field, + valOffset: ValueField) { + let obj = CacheIR::useObjectReg(objId); + let objSlots = CacheIR::allocateReg(); + let offset = CacheIR::allocateReg(); + let scratchVal = CacheIR::allocateValueReg(); + + CacheIR::addFailurePath(out label failure); + + let objOffsetAddr = Address::new(obj, NativeObject::offsetOfSlots as Int32); + emit MASM::LoadPtrAddress(objOffsetAddr, objSlots); + + CacheIR::emitLoadInt32StubField(offsetOffset, offset); + CacheIR::emitLoadValueStubField(valOffset, scratchVal); + + let slotVal = BaseIndex::new(objSlots, offset, Scale::TimesOne, 0_u32); + emit MASM::BranchTestValue( + Condition::NotEqual, slotVal, scratchVal, failure + ); + + CacheIR::releaseReg(objSlots); + CacheIR::releaseReg(offset); + CacheIR::releaseValueReg(scratchVal); + } + op GuardToString(valueId: ValueId) { let valueReg = CacheIR::useValueReg(valueId); @@ -660,6 +708,11 @@ ir CacheIR emits MASM { emit MASM::MoveValueImm(value, CacheIR::outputReg); } + op LoadUndefinedResult() { + let value = Value::getUndefined(); + emit MASM::MoveValueImm(value, CacheIR::outputReg); + } + op LoadBigIntResult(bigIntId: BigIntId) { let bigIntReg = CacheIR::useBigIntReg(bigIntId); emit MASM::TagValue(ValueType::BigInt, bigIntReg, CacheIR::outputReg); @@ -1119,6 +1172,11 @@ ir CacheIR emits MASM { emit MASM::Move32Imm32(CacheIR::readInt32Field(int32Field), dstReg); } + #[refined] + fn emitLoadValueStubField(valueField: ValueField, dstReg: ValueReg) emits MASM { + emit MASM::MoveValueImm(CacheIR::readValueField(valueField), dstReg); + } + #[refined] fn emitLoadObjectStubField(objectField: ObjectField, dstReg: Reg) emits MASM { emit MASM::MovePtrImmGCPtrObject(CacheIR::readObjectField(objectField), dstReg); diff --git a/notes/js.cachet b/notes/js.cachet index ae4f9ca..a1f651d 100644 --- a/notes/js.cachet +++ b/notes/js.cachet @@ -260,6 +260,14 @@ impl Value { Value::typeOf(value) == ValueType::Null } + fn getUndefined() -> Value { + let value = (unsafe { Value::getUndefinedUnchecked() }); + assume Value::isUndefined(value); + value + } + + unsafe fn getUndefinedUnchecked() -> Value; + fn isUndefined(value: Value) -> Bool { Value::typeOf(value) == ValueType::Undefined } diff --git a/notes/masm.cachet b/notes/masm.cachet index 41ed2bc..969f086 100644 --- a/notes/masm.cachet +++ b/notes/masm.cachet @@ -4,6 +4,7 @@ import "./js.cachet" struct Reg; +// Used for ValueOperand as well // #[cfg(cpp)] struct ValueReg; // #[cfg(verify)] @@ -115,6 +116,13 @@ impl BaseObjectSlotIndex { unsafe fn newUnchecked(inner: BaseValueIndex) -> BaseObjectSlotIndex; } +struct Operand; + +impl Operand { + fn fromBaseIndex(baseIndex: BaseIndex) -> Operand; + fn compareToReg(cond: Condition, operand: Operand, reg: Reg) -> Bool; +} + ir MASM { // NOTE: Needs to be implemented to call the equivalent // op with string as argument. @@ -482,6 +490,18 @@ ir MASM { } } + op BranchTestValue(cond: Condition, + lhs: BaseIndex, + rhs: ValueReg, + label branch: MASM) { + assert cond == Condition::Equal || cond == Condition::NotEqual; + let rhsReg = ValueReg::scratchReg(rhs); + let lhsOp = Operand::fromBaseIndex(lhs); + if Operand::compareToReg(cond, lhsOp, rhsReg) { + goto branch; + } + } + // Note: this is a wrapper around branchTestPtr for when // the register contains a TaggedProto op BranchTestNullProto(reg: Reg, label branch: MASM) { From 75f4090d807902081e30aaac7d2a1eb2d00322e5 Mon Sep 17 00:00:00 2001 From: David Thien Date: Thu, 3 Nov 2022 11:45:09 -0700 Subject: [PATCH 2/2] Work on implementing store ops --- notes/cacheir.cachet | 66 ++++++++++++++++++----- notes/js.cachet | 28 ++++++++++ notes/masm.cachet | 126 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 204 insertions(+), 16 deletions(-) diff --git a/notes/cacheir.cachet b/notes/cacheir.cachet index 61c49cf..ac3db64 100644 --- a/notes/cacheir.cachet +++ b/notes/cacheir.cachet @@ -6,12 +6,13 @@ enum OperandLocationKind { Uninitialized, PayloadReg, ValueReg, + Constant, } struct OperandLocation; impl OperandLocation { - fn kind(loc: OperandLocation) -> OperandLocationKind; + fn kind(loc: OperandLocation) -> OperandLocationKind; fn newUninitialized() -> OperandLocation { let loc = (unsafe { OperandLocation::newUninitializedUnchecked() }); @@ -22,7 +23,7 @@ impl OperandLocation { unsafe fn newUninitializedUnchecked() -> OperandLocation; fn setValueReg(out loc: OperandLocation, valueReg: ValueReg) { - (unsafe { OperandLocation::setValueRegUnchecked(out loc, valueReg) }); + (unsafe { OperandLocation::setValueRegUnchecked(out loc, valueReg) }); assume OperandLocation::kind(loc) == OperandLocationKind::ValueReg; assume (unsafe { OperandLocation::getValueRegUnchecked(loc) } ) == valueReg; } @@ -37,8 +38,16 @@ impl OperandLocation { unsafe fn getValueRegUnchecked(loc: OperandLocation) -> ValueReg; + fn getConstant(loc: OperandLocation) -> Value { + assert OperandLocation::kind(loc) == OperandLocationKind::Constant; + let val = (unsafe { OperandLocation::getConstantUnchecked(loc) }); + val + } + + unsafe fn getConstantUnchecked(loc: OperandLocation) -> Value; + fn setPayloadReg(out loc: OperandLocation, reg: Reg, type: ValueType) { - (unsafe { OperandLocation::setPayloadRegUnchecked(out loc, reg, type) }); + (unsafe { OperandLocation::setPayloadRegUnchecked(out loc, reg, type) }); assume OperandLocation::kind(loc) == OperandLocationKind::PayloadReg; assume (unsafe { OperandLocation::getPayloadRegUnchecked(loc) } ) == reg; assume (unsafe { OperandLocation::getPayloadTypeUnchecked(loc) } ) == type; @@ -106,13 +115,13 @@ impl TypedId { typedId } - unsafe fn fromValueIdUnchecked(valueId: ValueId, type: ValueType) -> TypedId; + unsafe fn fromValueIdUnchecked(valueId: ValueId, type: ValueType) -> TypedId; fn toValueId(typedId: TypedId) -> ValueId { assert TypedId::isValueId(typedId); let type = TypedId::type(typedId); let valueId = (unsafe { TypedId::toValueIdUnchecked(typedId) }); - assume (unsafe { TypedId::fromValueIdUnchecked(valueId, type) }) == typedId; + assume (unsafe { TypedId::fromValueIdUnchecked(valueId, type) }) == typedId; valueId } @@ -356,7 +365,7 @@ ir CacheIR emits MASM { op GuardClass(objId: ObjectId, kind: GuardClassKind) { let objReg = CacheIR::useObjectReg(objId); let scratchReg = CacheIR::allocateReg(); - + CacheIR::addFailurePath(out label failure); if kind == GuardClassKind::JSFunction { @@ -490,8 +499,8 @@ ir CacheIR emits MASM { } op GuardDynamicSlotValue(objId: ObjectId, - offsetOffset: Int32Field, - valOffset: ValueField) { + offsetOffset: Int32Field, + valOffset: ValueField) { let obj = CacheIR::useObjectReg(objId); let objSlots = CacheIR::allocateReg(); let offset = CacheIR::allocateReg(); @@ -754,6 +763,16 @@ ir CacheIR emits MASM { bind done; } + op StoreFixedSlot(objId: ObjectId, offsetOffset: Int32Field, ValueId) { + let obj = CacheIR::useObjectReg(objId); + let offset = readInt32Field(offsetOffset); + let val = CacheIR::useConstantOrReg(objId); + let scratch = CacheIR::allocateReg(); + + let slot = Address::new(obj, offset); + emit MASM::GuardedCallPreBarrier(slot, MIRType::Value) + } + op Int32AddResult(lhsId: Int32Id, rhsId: Int32Id) { let lhsReg = CacheIR::useInt32Reg(lhsId); let rhsReg = CacheIR::useInt32Reg(rhsId); @@ -876,7 +895,7 @@ ir CacheIR emits MASM { emit MASM::TagValue(ValueType::Int32, scratchReg, CacheIR::outputReg); - CacheIR::releaseReg(scratchReg); + CacheIR::releaseReg(scratchReg); } op Int32BitOrResult(lhsId: Int32Id, rhsId: Int32Id) { @@ -946,7 +965,7 @@ ir CacheIR emits MASM { let scratchReg = CacheIR::allocateReg(); CacheIR::addFailurePath(out label failure); - + emit MASM::Mov(lhsReg, scratchReg); emit MASM::FlexibleRshift32(rhsReg, scratchReg); emit MASM::BranchTest32(Condition::Signed, scratchReg, scratchReg, failure); @@ -1050,7 +1069,7 @@ ir CacheIR emits MASM { let valueId = id as ValueId; CacheIR::defineValueReg(valueId) } - + #[refined] fn defineBooleanReg(id: BooleanId) -> Reg { let typedId = TypedId::fromBooleanId(id); @@ -1068,7 +1087,7 @@ ir CacheIR emits MASM { let typedId = TypedId::fromSymbolId(id); CacheIR::defineReg(typedId) } - + #[refined] fn defineBigIntReg(id: BigIntId) -> Reg { let typedId = TypedId::fromBigIntId(id); @@ -1080,7 +1099,7 @@ ir CacheIR emits MASM { #[refined] fn useReg(id: TypedId) emits MASM -> Reg { - let operandId = id as OperandId; + let operandId = id as OperandId; let location = CacheIR::getOperandLocation(operandId); let locationKind = OperandLocation::kind(location); @@ -1103,7 +1122,7 @@ ir CacheIR emits MASM { #[refined] fn useValueReg(valueId: ValueId) emits MASM -> ValueReg { - let operandId = valueId as OperandId; + let operandId = valueId as OperandId; let location = CacheIR::getOperandLocation(operandId); let locationKind = OperandLocation::kind(location); @@ -1129,6 +1148,25 @@ ir CacheIR emits MASM { CacheIR::useReg(typedId) } + fn useConstantOrReg(id: ValueId) -> ConstantOrReg { + let operandId = id as OperandId; + let location = CacheIR::getOperandLocation(operandId); + let locationKind = OperandLocation::kind(location); + + if locationKind == OperandLocationKind::Constant { + let c = OperandLocation::getConstant(location); + return ConstantOrReg::fromValue(c); + } else if locationKind == OperandLocationKind::PayloadReg { + let r = OperandLocation::getValueReg(location); + return ConstantOrReg::fromValueReg(r); + } else { + assert false; + } + + // TODO: unecessary fallthrough? + return ConstantOrReg::fromValueReg(CacheIR::useValueReg(id)); + } + #[refined] fn useInt32Reg(id: Int32Id) emits MASM -> Reg { let typedId = TypedId::fromInt32Id(id); diff --git a/notes/js.cachet b/notes/js.cachet index a1f651d..94ad4a8 100644 --- a/notes/js.cachet +++ b/notes/js.cachet @@ -181,6 +181,34 @@ enum ValueType { Unknown, } +enum MIRType { + Undefined, + Null, + Boolean, + Int32, + Int64, + IntPtr, + Double, + Float32, + String, + Symbol, + BigInt, + Simd128, + Object, + MagicOptimizedOut, + MagicHole, + MagicIsConstructing, + MagicUninitializedLexical, + Value, + None, + Slots, + Elements, + Pointer, + RefOrNull, + StackResults, + Shape, +} + struct Value; impl Value { diff --git a/notes/masm.cachet b/notes/masm.cachet index 969f086..64ebf63 100644 --- a/notes/masm.cachet +++ b/notes/masm.cachet @@ -4,6 +4,13 @@ import "./js.cachet" struct Reg; +struct ConstantOrReg; + +impl ConstantOrReg { + fn fromValue(v: Value) -> ConstantOrReg; + fn fromValueReg(v: ValueReg) -> ConstantOrReg; +} + // Used for ValueOperand as well // #[cfg(cpp)] struct ValueReg; @@ -26,6 +33,10 @@ enum Condition { LessThan, } +struct AbsoluteAddress { + addr: UInt64, +} + struct Address { base: Reg, offset: Int32, @@ -120,6 +131,7 @@ struct Operand; impl Operand { fn fromBaseIndex(baseIndex: BaseIndex) -> Operand; + fn fromAbsoluteAddress(addr: AbsoluteAddress) -> Operand; fn compareToReg(cond: Condition, operand: Operand, reg: Reg) -> Bool; } @@ -490,6 +502,13 @@ ir MASM { } } + op BranchTestGCThing(cond: Condition, addr: Address, label done: MASM) { + assert cond == Condition::Equal || cond == Condition::NotEqual; + if MASM::testGCThingAddr(cond, addr) { + goto done; + } + } + op BranchTestValue(cond: Condition, lhs: BaseIndex, rhs: ValueReg, @@ -722,7 +741,7 @@ ir MASM { } op Branch32AddressImm32( - condition: Condition, address: Address, rhsInt32: Int32, label branch:MASM + condition: Condition, address: Address, rhsInt32: Int32, label branch: MASM ) { assert ( condition == Condition::Equal || @@ -730,7 +749,6 @@ ir MASM { condition == Condition::LessThan ); - let baseData = MASM::getData(address.base); let data = RegData::readData(baseData, address.offset); let lhsInt32 = Value::toInt32(RegData::toUnboxedValue(data)); @@ -744,6 +762,18 @@ ir MASM { } } + op BranchPtrAbsAddrImm64( + cond: Condition, addr: AbsoluteAddress, imm: UInt64, label branch: MASM + ) { + assert cond == Condition::Equal || cond == Condition::NotEqual; + + if cond == Condition::Equal && addr.addr == imm { + goto branch; + } else if cond == Condition::NotEqual && addr.addr != imm { + goto branch; + } + } + op BranchTest32(condition: Condition, lhsReg: Reg, rhsReg: Reg, label branch: MASM) { let lhsInt32 = MASM::getInt32(lhsReg); let rhsInt32 = MASM::getInt32(rhsReg); @@ -807,6 +837,68 @@ ir MASM { } } + op BranchTest32AbsAddrImm32(condition: Condition, + lhsAddr: AbsoluteAddress, + rhsImm: Int32, + label branch: MASM) { + assert ( + condition == Condition::Zero || + condition == Condition::NonZero || + condition == Condition::Signed || + condition == Condition::NotSigned + ); + + // TODO: we don't really have to check the address right now because we + // only allow a small set of conditions, but will need to check for + // other conditions + if MASM::isAddressImmediate(lhsAddr.addr) { + // The address fits in 32-bits, so directly compare + let result = (lhsAddr.addr as UInt32) & (rhsImm as UInt32); + if condition == Condition::Zero { + if result == 0_i32 { + goto branch; + } + } else if condition == Condition::NonZero { + if result != 0_i32 { + goto branch; + } + } else if condition == Condition::Signed { + if result < 0_i32 { + goto branch; + } + } else if condition == Condition::NotSigned { + if result > 0_i32 { + goto branch; + } + } + } else { + let result = lhsAddr.addr & (rhsImm as UInt64); + if condition == Condition::Zero { + if result == 0_i32 { + goto branch; + } + } else if condition == Condition::NonZero { + if result != 0_i32 { + goto branch; + } + } else if condition == Condition::Signed { + if result < 0_i32 { + goto branch; + } + } else if condition == Condition::NotSigned { + if result > 0_i32 { + goto branch; + } + } + } + } + + op BranchTestNeedsIncrementalBarrier(cond: Condition, label l: MASM) { + assert cond == Condition::Zero || cond == Condition::NonZero; + needsBarrierAddr = MASM::addressOfNeedsIncrementalBarrier(); + MASM::BranchTest32AbsAddrImm32(cond, needsBarrierAddr, 1_i32, l) + } + op BranchAdd32(condition: Condition, srcReg: Reg, dstReg: Reg, label branch: MASM) { let lhsInt32 = MASM::getInt32(srcReg); let rhsInt32 = MASM::getInt32(dstReg); @@ -1039,6 +1131,24 @@ ir MASM { MASM::setData(protoReg, data); } + op GuardedCallPreBarrier(addr: Address, ty: MIRType) { + label done: MASM; + BranchTestNeedsIncrementalBarrier(Condition::Zero, done); + UnguardedCallPreBarrier(addr, ty); + bind done; + } + + op UnguardedCallPreBarrier(addr: Address, ty: MIRType) { + label done: MASM; + if ty == MIRType::Value { + BranchTestGCThing(Condition::NotEqual, addr, done); + } else if ty == MIRType::Object || ty == MIRType::String { + // TODO: This is really trying to compare pointer types, but we'll + // list them as uint64 for now + BranchPtrAbsAddrImm64(Condition::Equal, addr, 0_u64, done); + } + } + fn getData(reg: Reg) -> RegData; fn setData(reg: Reg, data: RegData); @@ -1062,6 +1172,18 @@ ir MASM { fn getBigInt(reg: Reg) -> BigInt; fn setBigInt(reg: Reg, bigInt: BigInt); + + fn addressOfNeedsIncrementalBarrier() -> UInt32; + #[refined] + fn isAddressImmediate(addr: UInt64) -> Bool { + // checks if the address fits into a UInt32 + if addr <= 4294967295_u64 { + return true; + } else { + return false; + } + } + fn testGCThingAddr(cond: Condition, addr: Address) -> Bool; } //impl MASM {