feat(asm): Phase C.0 — add inline_asm IR op (lock, no behavior change)
Adds the `inline_asm: InlineAsm` opcode to the IR Op union (inst.zig): interned template + operand list (role/name/constraint/operand) + interned clobber names + has_side_effects; the result rides on Inst.ty (void / scalar / tuple). The new variant forces coverage in the exhaustive Op switches: - interp.zig: loud bailDetail — inline asm is never comptime-evaluable. - print.zig: an IR-dump arm. - emit_llvm.zig: a @panic TRIPWIRE — emit lands in Phase D, and until then lowerAsmExpr still bails, so no inline_asm op is ever created. Reaching emit would mean lowering switched over before emit was ready; crash loudly rather than miscompile. No behavior change: lowering still bails, the op is constructed only in the new `inline_asm op shape` unit test (inst.test.zig). zig build test green (652 corpus, 446 unit).
This commit is contained in:
@@ -10,6 +10,8 @@ const FuncId = inst_mod.FuncId;
|
||||
const Inst = inst_mod.Inst;
|
||||
const Block = inst_mod.Block;
|
||||
const Function = inst_mod.Function;
|
||||
const InlineAsm = inst_mod.InlineAsm;
|
||||
const StringId = types.StringId;
|
||||
|
||||
test "Ref none sentinel" {
|
||||
try std.testing.expect(Ref.none.isNone());
|
||||
@@ -48,6 +50,40 @@ test "block creation" {
|
||||
try std.testing.expectEqual(@as(usize, 2), block.insts.items.len);
|
||||
}
|
||||
|
||||
test "inline_asm op shape (ASM stream Phase C.0)" {
|
||||
// out_value (yields the value, operand = .none) + a named-less input,
|
||||
// plus two clobbers; result rides on Inst.ty.
|
||||
const operands = [_]InlineAsm.AsmOperand{
|
||||
.{ .role = .out_value, .name = @enumFromInt(1), .constraint = @enumFromInt(2), .operand = Ref.none },
|
||||
.{ .role = .input, .name = .empty, .constraint = @enumFromInt(3), .operand = Ref.fromIndex(5) },
|
||||
};
|
||||
const clobbers = [_]StringId{ @enumFromInt(4), @enumFromInt(6) };
|
||||
const inst = Inst{
|
||||
.op = .{ .inline_asm = .{
|
||||
.template = @enumFromInt(10),
|
||||
.operands = &operands,
|
||||
.clobbers = &clobbers,
|
||||
.has_side_effects = true,
|
||||
} },
|
||||
.ty = .i64,
|
||||
};
|
||||
switch (inst.op) {
|
||||
.inline_asm => |a| {
|
||||
try std.testing.expect(a.has_side_effects);
|
||||
try std.testing.expectEqual(@as(usize, 2), a.operands.len);
|
||||
try std.testing.expectEqual(@as(usize, 2), a.clobbers.len);
|
||||
try std.testing.expectEqual(InlineAsm.AsmOperand.Role.out_value, a.operands[0].role);
|
||||
// an out_value operand carries no input Ref — the asm yields it
|
||||
try std.testing.expect(a.operands[0].operand.isNone());
|
||||
try std.testing.expectEqual(InlineAsm.AsmOperand.Role.input, a.operands[1].role);
|
||||
try std.testing.expectEqual(Ref.fromIndex(5), a.operands[1].operand);
|
||||
// an anonymous operand uses the `.empty` StringId sentinel
|
||||
try std.testing.expectEqual(StringId.empty, a.operands[1].name);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
test "function creation" {
|
||||
const alloc = std.testing.allocator;
|
||||
const params = &[_]Function.Param{
|
||||
|
||||
Reference in New Issue
Block a user