Repository navigation
[objects] Add inline JIT support for native object model - #738
Conversation
| return; | ||
| } | ||
| var index = popReg(); | ||
| if (checkForConstNull(state.peek())) return; |
There was a problem hiding this comment.
We should also support hardware null checks, which means handling a SIGSEGV and having metadata here.
| var decl = ArrayDecl.!(module.heaptypes[ht_index]); | ||
| emit_call_runtime_op1n(Opcode.ARRAY_GET_U, ht_index, 2, [decl.elem_types[0].valtype], true); | ||
| var vt = decl.elem_types[0].valtype; | ||
| if (!inlineGcAccess()) { |
| def emit_breq_r_i(reg: Reg, val: int, label: MasmLabel); | ||
| def emit_breq_r_l(reg: Reg, val: int, label: MasmLabel); | ||
| def emit_brne_r_i(reg: Reg, val: int, label: MasmLabel); | ||
| def emit_breq_r_r(r1: Reg, r2: Reg, label: MasmLabel); // full-width comparison |
There was a problem hiding this comment.
We should probably be moving these to a MasmCmpBr enum.
| emit_call_runtime_op2n(op, struct_index, field_index, 1, [vt], true); | ||
| return; | ||
| } | ||
| if (checkForConstNull(state.peek())) return; |
There was a problem hiding this comment.
This should probably be emitNullCheck and it returns one of three outcomes, statically failing, dynamic check, or statically non-null.
| } | ||
| } | ||
| // Drops the {count} operands of an allocation and pushes the new object in {obj}. | ||
| private def pushNewObject(count: u32, obj: Reg) { |
There was a problem hiding this comment.
Inline this; it's just a weird factoring to drop N things and then push a reference. It's better to have a helper to push a reg.
| } | ||
| // Bump-allocates {size} bytes, or {size_reg} bytes if not {NO_REG}, into {dst}, branching to {slow} | ||
| // if the allocation space is exhausted. Uses Virgil's allocation pointers. | ||
| private def emitBump(masm: MacroAssembler, dst: Reg, size: int, size_reg: Reg, t1: Reg, t2: Reg, slow: MasmLabel) { |
There was a problem hiding this comment.
Should be in the Macro assembler.
|
|
||
| def NO_REG = SpcConsts.NO_REG; | ||
| def REF_SIZE = RiGc.REF_SIZE; | ||
| def fieldStorage(kind: NativeFieldKind, signed: bool) -> MemStorageKind { |
There was a problem hiding this comment.
This should probably be part of FieldStorage, since it doesn't depend on this object model.
| _ => return access; | ||
| } | ||
| } | ||
| def log2(size: int) -> int { |
| while ((1 << u5.view(r)) < size) r++; | ||
| return r; | ||
| } | ||
| def isGpr(kind: ValueKind) -> bool { |
There was a problem hiding this comment.
Special case for GPR versus not. Should use register allocator, which probably needs to be part of the compiler interface.
| def newStruct(decl: StructDecl) -> NativeWasmStruct { | ||
| return NativeWasmStruct.new(getStructInfo(decl)); | ||
| var info = getInfo(decl); | ||
| if (info == null) failLayout(decl, StringBuilder.new().put1("too large for native object model (limit is %d words)", MAX_WORDS).toString()); |
There was a problem hiding this comment.
Let's return null and make the runtime trap, so that we can make test expectations and not crash the VM.
Adds an ObjectModelCompiler interface that object models can implement to emit inline code for struct/array accesses, allocations, and casts, and an implementation for the native object model: - struct.get/set, array.get/set/len with inline field and element accesses - struct.new(_default), array.new_fixed/default with a direct allocation call and inline initialization, or inline bump allocation - ref.test/cast and br_on_cast(_fail) with null/i31 tests and descriptor comparison, with a display for subtypes of non-final structs - optional hardware null checks Flags: --inline-gc-access, --inline-gc-alloc, --inline-gc-bump, --hardware-null-checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8fc44c0 to
43ac41c
Compare
No description provided.