feat: support existing io.print formatting

This commit is contained in:
2026-11-01 11:37:46 +01:00
parent e036070bf2
commit 1f457d6b41
23 changed files with 895 additions and 116 deletions
+66 -7
View File
@@ -16,6 +16,7 @@ float_opcodes :: $enummap.init_default(ir.Op, []u8, "", {
subtract = "fsub",
multiply = "fmul",
divide = "fdiv",
remainder = "frem",
})
@hide:file
@@ -329,15 +330,50 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
for (0..instruction.arguments_count) |index| {
_ = try load(state, block.arguments.items[instruction.arguments_start + index])
}
# C default promotions apply only after the fixed prefix.
first_promotion :: state.next_register
if (instruction.variadic) {
for (instruction.fixed_arguments..instruction.arguments_count) |index| {
operand :: block.arguments.items[instruction.arguments_start + index]
kind :: state.values[usize(operand)].kind
if (variadic_type(kind) == kind) continue
promoted :: fresh(state)
opcode []u8 :: if (kind == .bool or ir.unsigned_integer(kind)) "zext" else "sext"
try io.print(" %r{} = {s} ", output, {promoted, opcode})
try write_type(output, state.module, state.values[usize(operand)])
try io.print(" %r{} to i32\n", output, {first_argument + index})
}
}
if (instruction.type != .void) try io.print(" %r{} = call ", output, {result})
else try io.write_all(output, " call ")
try write_abi_result(output, state.module, type)
if (instruction.variadic) {
try io.write_all(output, " (")
for (0..instruction.fixed_arguments) |index| {
if (index != 0) try io.write_all(output, ", ")
operand :: block.arguments.items[instruction.arguments_start + index]
try write_type(output, state.module, state.values[usize(operand)])
}
if (instruction.fixed_arguments != 0) try io.write_all(output, ", ")
try io.write_all(output, "...)")
}
try io.print(" %r{}(", output, {callee})
promotion := first_promotion
for (0..instruction.arguments_count) |index| {
if (index != 0) try io.write_all(output, ", ")
operand :: block.arguments.items[instruction.arguments_start + index]
try write_abi_parameter(output, state.module, state.values[usize(operand)])
try io.print(" %r{}", output, {first_argument + index})
argument_type := state.values[usize(operand)]
register := first_argument + index
if (instruction.variadic and index >= instruction.fixed_arguments) {
kind :: variadic_type(argument_type.kind)
if (kind != argument_type.kind) {
register = promotion
promotion += 1
argument_type.kind = kind
}
}
try write_abi_parameter(output, state.module, argument_type)
try io.print(" %r{}", output, {register})
}
try io.write_all(output, ")\n")
}
@@ -372,18 +408,24 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
try io.print(" %r{} = {s} i{} %r{}, %r{}\n", output, {result, opcode, ir.integer_bits(instruction.type), left, right})
}
.shift_left, .shift_right, .shift_left_saturating: result = try emit_shift(state, instruction)
.add, .subtract, .multiply, .divide: {
.add, .subtract, .multiply, .divide, .remainder: {
left :: try load(state, instruction.left)
right :: try load(state, instruction.right)
if (instruction.type == .float) {
if (instruction.op == .remainder) {
zero :: fresh(state)
try io.print(" %r{} = fcmp oeq double %r{}, 0.0\n", output, {zero, right})
try check_failure(state, zero, .division_by_zero, instruction.span)
}
opcode :: enummap.get(&float_opcodes, instruction.op)?
try io.print(" %r{} = {s} double %r{}, %r{}\n", output, {result, opcode, left, right})
} else if (instruction.op == .divide) {
} else if (instruction.op == .divide or instruction.op == .remainder) {
bits :: ir.integer_bits(instruction.type)
zero :: fresh(state)
try io.print(" %r{} = icmp eq i{} %r{}, 0\n", output, {zero, bits, right})
try check_failure(state, zero, .division_by_zero, instruction.span)
if (!ir.unsigned_integer(instruction.type)) {
divisor := right
if (!ir.unsigned_integer(instruction.type) and instruction.op == .divide) {
minimum :: fresh(state)
negative_one :: fresh(state)
overflow :: fresh(state)
@@ -392,8 +434,17 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
try io.print(" %r{} = and i1 %r{}, %r{}\n", output, {overflow, minimum, negative_one})
try check_failure(state, overflow, .integer_overflow, instruction.span)
}
opcode []u8 :: if (ir.unsigned_integer(instruction.type)) "udiv" else "sdiv"
try io.print(" %r{} = {s} i{} %r{}, %r{}\n", output, {result, opcode, bits, left, right})
if (!ir.unsigned_integer(instruction.type) and instruction.op == .remainder) {
negative_one :: fresh(state)
divisor = fresh(state)
try io.print(" %r{} = icmp eq i{} %r{}, -1\n", output, {negative_one, bits, right})
# LLVM srem is poison for min/-1; every signed value rem -1 is zero.
try io.print(" %r{} = select i1 %r{}, i{} 1, i{} %r{}\n", output, {divisor, negative_one, bits, bits, right})
}
opcode []u8 := if (ir.unsigned_integer(instruction.type)) "udiv" else "sdiv"
if (instruction.op == .remainder)
opcode = if (ir.unsigned_integer(instruction.type)) "urem" else "srem"
try io.print(" %r{} = {s} i{} %r{}, %r{}\n", output, {result, opcode, bits, left, divisor})
} else {
opcode :: enummap.get(&integer_opcodes, instruction.op)?
bits :: ir.integer_bits(instruction.type)
@@ -700,3 +751,11 @@ emit_conversion proc(state @mut State, instruction ir.Instruction) usize ! io.Wr
try io.print(" %r{} = {s} i{} %r{} to i{}\n", output, {result, opcode, source_bits, operand, destination_bits})
return result
}
@hide:file
variadic_type proc(type ir.Type) ir.Type {
match type {
.bool, .i8, .u8, .i16, .u16: return .i32
else: return type
}
}
+6 -2
View File
@@ -392,7 +392,7 @@ emit_array_printer proc(output io.Writer, module @ir.Module, id ir.ArrayId) void
try write_type(output, module, type)
try io.write_all(output, "\n store ")
try write_type(output, module, type)
try io.write_all(output, " %value, ptr %storage\n call void @out(i32 1, ptr @open, i64 1)\n br label %loop\nloop:\n %index = phi i64 [ 0, %entry ], [ %next, %element ]\n")
try io.write_all(output, " %value, ptr %storage\n call void @out(i32 1, ptr @honey.array_open, i64 1)\n br label %loop\nloop:\n %index = phi i64 [ 0, %entry ], [ %next, %element ]\n")
try io.print(" %done = icmp eq i64 %index, {}\n", output, {shape.len})
try io.write_all(output, " br i1 %done, label %end, label %separator\nseparator:\n %first = icmp eq i64 %index, 0\n br i1 %first, label %element, label %comma\ncomma:\n call void @out(i32 1, ptr @separator, i64 2)\n br label %element\nelement:\n %pointer = getelementptr ")
try write_type(output, module, type)
@@ -402,7 +402,7 @@ emit_array_printer proc(output io.Writer, module @ir.Module, id ir.ArrayId) void
# functions may occur inside arrays without being printable source results.
if (element.kind != .function) try print_call(output, module, element, 0)
else try io.write_all(output, " call void @exit(i32 2)\n")
try io.write_all(output, " %next = add i64 %index, 1\n br label %loop\nend:\n call void @out(i32 1, ptr @close, i64 1)\n ret void\n}\n")
try io.write_all(output, " %next = add i64 %index, 1\n br label %loop\nend:\n call void @out(i32 1, ptr @honey.array_close, i64 1)\n ret void\n}\n")
}
@hide:file
@@ -614,6 +614,10 @@ emit_function proc(state @mut State, function @ir.Function, id usize) void ! io.
try write_abi_parameter(output, state.module, local_type(function, index))
try io.print(" %p{}", output, {index})
}
if (function.variadic) {
if (function.parameters.items.len != 0) try io.write_all(output, ", ")
try io.write_all(output, "...")
}
if (function.external_name.len != 0) {
try io.write_all(output, ")\n")
return
+4 -4
View File
@@ -43,13 +43,13 @@ runtime ::
`@error = private constant [9 x i8] c": error: "
`@runtime_trap = private constant [16 x i8] c": runtime trap: "
`@newline = private constant [1 x i8] c"\0A"
`@open = private constant [1 x i8] c"["
`@close = private constant [1 x i8] c"]"
`@honey.array_open = private constant [1 x i8] c"["
`@honey.array_close = private constant [1 x i8] c"]"
`@record_open = private constant [1 x i8] c"{"
`@record_close = private constant [1 x i8] c"}"
`@separator = private constant [2 x i8] c", "
`@write_error = private constant [44 x i8] c": infrastructure error: cannot write output\0A"
`@read = private constant [42 x i8] c"local value was read before initialization"
`@honey.uninitialized_read = private constant [42 x i8] c"local value was read before initialization"
`@assign = private constant [46 x i8] c"local value was assigned before initialization"
`@cycle = private constant [27 x i8] c"global initialization cycle"
`@overflow = private constant [27 x i8] c"integer arithmetic overflow"
@@ -154,7 +154,7 @@ runtime ::
` %ready = load i1, ptr %flag
` br i1 %ready, label %end, label %fail
`fail:
` %message = select i1 %read, ptr @read, ptr @assign
` %message = select i1 %read, ptr @honey.uninitialized_read, ptr @assign
` %length = select i1 %read, i64 42, i64 46
` call void @trap(i64 -1, i64 %source, i64 %line, i64 %column, ptr %message, i64 %length)
` unreachable