Skip to content

[objects] Add inline JIT support for native object model - #738

Merged
titzer merged 1 commit into
masterfrom
native_obj_jit
Oct 31, 2026
Merged

titzer merged 1 commit into
masterfrom
native_obj_jit

Conversation

@titzer

@titzer titzer commented Oct 31, 2026

Copy link
Copy Markdown
Owner

No description provided.

return;
}
var index = popReg();
if (checkForConstNull(state.peek())) return;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One liner.

Comment thread src/engine/compiler/MacroAssembler.v3 Outdated
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

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be part of FieldStorage, since it doesn't depend on this object model.

_ => return access;
}
}
def log2(size: int) -> int {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is Ints.log

while ((1 << u5.view(r)) < size) r++;
return r;
}
def isGpr(kind: ValueKind) -> bool {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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());

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@titzer
titzer merged commit 96b1ce2 into master Oct 31, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant