feat: complete memory representation gate
This commit is contained in:
@@ -178,6 +178,9 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
}
|
||||
.pointer_address: {
|
||||
pointer :: try load(state, instruction.left)
|
||||
missing :: fresh(state)
|
||||
try io.print(" %r{} = icmp eq ptr %r{}, null\n", output, {missing, pointer})
|
||||
try check_failure(state, missing, .invalid_pointer_access, instruction.span)
|
||||
address :: fresh(state)
|
||||
try io.print(" %r{} = insertvalue %address poison, ptr %r{}, 0\n", output, {address, pointer})
|
||||
try io.print(" %r{} = insertvalue %address %r{}, ptr null, 1\n", output, {result, address})
|
||||
@@ -189,6 +192,56 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
try write_type(output, state.module, Type{ kind = instruction.pointee, shape = instruction.array_type })
|
||||
try io.print(", ptr %r{}, i64 %r{}\n", output, {pointer, index})
|
||||
}
|
||||
.pointer_cast: result = try load(state, instruction.left)
|
||||
.string_pointer, .string_length: {
|
||||
string :: try load(state, instruction.left)
|
||||
field usize :: if (instruction.op == .string_pointer) 0 else 1
|
||||
try io.print(" %r{} = extractvalue %string %r{}, {}\n", output, {result, string, field})
|
||||
}
|
||||
.optional_check: {
|
||||
operand :: try load(state, instruction.left)
|
||||
failed :: fresh(state)
|
||||
if (state.values[usize(instruction.left)].kind == .bool) {
|
||||
try io.print(" %r{} = xor i1 %r{}, true\n", output, {failed, operand})
|
||||
} else try io.print(" %r{} = icmp eq ptr %r{}, null\n", output, {failed, operand})
|
||||
try check_failure(state, failed, .null_unwrap, instruction.span)
|
||||
return
|
||||
}
|
||||
.slice_check: {
|
||||
start :: try load(state, instruction.left)
|
||||
end :: try load(state, instruction.right)
|
||||
reversed :: fresh(state)
|
||||
try io.print(" %r{} = icmp ugt i64 %r{}, %r{}\n", output, {reversed, start, end})
|
||||
failed := reversed
|
||||
if (instruction.extra != ir.no_value) {
|
||||
bound :: try load(state, instruction.extra)
|
||||
outside :: fresh(state)
|
||||
failed = fresh(state)
|
||||
try io.print(" %r{} = icmp ugt i64 %r{}, %r{}\n", output, {outside, end, bound})
|
||||
try io.print(" %r{} = or i1 %r{}, %r{}\n", output, {failed, reversed, outside})
|
||||
}
|
||||
try check_failure(state, failed, .invalid_slice_bounds, instruction.span)
|
||||
return
|
||||
}
|
||||
.sentinel_check: {
|
||||
pointer :: try load(state, instruction.left)
|
||||
expected :: try load(state, instruction.right)
|
||||
missing :: fresh(state)
|
||||
try io.print(" %r{} = icmp eq ptr %r{}, null\n", output, {missing, pointer})
|
||||
try check_failure(state, missing, .invalid_pointer_access, instruction.span)
|
||||
actual :: fresh(state)
|
||||
element :: Type{ kind = instruction.pointee, shape = instruction.array_type }
|
||||
try io.print(" %r{} = load ", output, {actual})
|
||||
try write_type(output, state.module, element)
|
||||
try io.print(", ptr %r{}, align 1\n", output, {pointer})
|
||||
failed :: try sentinel_difference(state, element, actual, expected)
|
||||
try check_failure(state, failed, .sentinel_mismatch, instruction.span)
|
||||
return
|
||||
}
|
||||
.memory_copy, .memory_set: {
|
||||
try emit_memory(state, instruction)
|
||||
return
|
||||
}
|
||||
.load_address, .store_address: {
|
||||
address :: try load(state, instruction.left)
|
||||
pointer :: fresh(state)
|
||||
@@ -204,7 +257,7 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
}
|
||||
try io.print(" %r{} = load ", output, {result})
|
||||
try write_type(output, state.module, type)
|
||||
try io.print(", ptr %r{}\n", output, {pointer})
|
||||
try io.print(", ptr %r{}, align 1\n", output, {pointer})
|
||||
}
|
||||
.call: {
|
||||
callee :: try load(state, instruction.left)
|
||||
@@ -216,12 +269,12 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
}
|
||||
if (instruction.type != .void) try io.print(" %r{} = call ", output, {result})
|
||||
else try io.write_all(output, " call ")
|
||||
try write_type(output, state.module, type)
|
||||
try write_abi_result(output, state.module, type)
|
||||
try io.print(" %r{}(", output, {callee})
|
||||
for (0..instruction.arguments_count) |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)])
|
||||
try write_abi_parameter(output, state.module, state.values[usize(operand)])
|
||||
try io.print(" %r{}", output, {first_argument + index})
|
||||
}
|
||||
try io.write_all(output, ")\n")
|
||||
@@ -314,6 +367,130 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
try store(state, instruction.destination, result)
|
||||
}
|
||||
|
||||
#! compares the complete sentinel value, including nested aggregate fields.
|
||||
@hide:file
|
||||
sentinel_difference proc(state @mut State, type Type, actual, expected usize) usize ! io.WriteError {
|
||||
output :: state.output
|
||||
if (ir.aggregate(type.kind)) {
|
||||
shape :: state.module.arrays.items[usize(type.shape)]
|
||||
count usize :: if (shape.is_union) divtrunc!(shape.byte_size, 8) else if (shape.kind == .record) shape.fields.len else shape.len
|
||||
result := fresh(state)
|
||||
try io.print(" %r{} = icmp ne i1 false, false\n", output, {result})
|
||||
for (0..count) |index| {
|
||||
child :: if (shape.is_union) Type{ kind = .u64 } else if (shape.kind == .record) Type{ kind = shape.fields[index].type, shape = shape.fields[index].array_type } else Type{ kind = shape.element, shape = shape.element_array }
|
||||
left :: fresh(state)
|
||||
right :: fresh(state)
|
||||
try io.print(" %r{} = extractvalue ", output, {left})
|
||||
try write_type(output, state.module, type)
|
||||
try io.print(" %r{}, ", output, {actual})
|
||||
if (shape.is_union) try io.write_all(output, "0, ")
|
||||
try io.print("{}\n %r{} = extractvalue ", output, {index, right})
|
||||
try write_type(output, state.module, type)
|
||||
try io.print(" %r{}, ", output, {expected})
|
||||
if (shape.is_union) try io.write_all(output, "0, ")
|
||||
try io.print("{}\n", output, {index})
|
||||
different :: try sentinel_difference(state, child, left, right)
|
||||
combined :: fresh(state)
|
||||
try io.print(" %r{} = or i1 %r{}, %r{}\n", output, {combined, result, different})
|
||||
result = combined
|
||||
}
|
||||
return result
|
||||
}
|
||||
if (type.kind == .string) {
|
||||
left_pointer :: fresh(state)
|
||||
right_pointer :: fresh(state)
|
||||
left_length :: fresh(state)
|
||||
right_length :: fresh(state)
|
||||
try io.print(" %r{} = extractvalue %string %r{}, 0\n", output, {left_pointer, actual})
|
||||
try io.print(" %r{} = extractvalue %string %r{}, 0\n", output, {right_pointer, expected})
|
||||
try io.print(" %r{} = extractvalue %string %r{}, 1\n", output, {left_length, actual})
|
||||
try io.print(" %r{} = extractvalue %string %r{}, 1\n", output, {right_length, expected})
|
||||
pointer_difference :: try sentinel_difference(state, Type{ kind = .pointer }, left_pointer, right_pointer)
|
||||
length_difference :: try sentinel_difference(state, Type{ kind = .usize }, left_length, right_length)
|
||||
result :: fresh(state)
|
||||
try io.print(" %r{} = or i1 %r{}, %r{}\n", output, {result, pointer_difference, length_difference})
|
||||
return result
|
||||
}
|
||||
result :: fresh(state)
|
||||
if (type.kind == .void) {
|
||||
try io.print(" %r{} = icmp ne i1 false, false\n", output, {result})
|
||||
return result
|
||||
}
|
||||
opcode []u8 :: if (type.kind == .float) "fcmp une" else "icmp ne"
|
||||
try io.print(" %r{} = {s} ", output, {result, opcode})
|
||||
try write_type(output, state.module, type)
|
||||
try io.print(" %r{}, %r{}\n", output, {actual, expected})
|
||||
return result
|
||||
}
|
||||
|
||||
@hide:file
|
||||
emit_memory proc(state @mut State, instruction ir.Instruction) void ! io.WriteError {
|
||||
output :: state.output
|
||||
element :: Type{ kind = instruction.pointee, shape = instruction.array_type }
|
||||
width :: ir.storage_size(state.module, element.kind, element.shape)
|
||||
if (width == 0) return
|
||||
destination :: try load(state, instruction.left)
|
||||
operand :: try load(state, instruction.right)
|
||||
count :: try load(state, instruction.extra)
|
||||
size := count
|
||||
if (width != 1) {
|
||||
pair :: fresh(state)
|
||||
overflow :: fresh(state)
|
||||
size = fresh(state)
|
||||
try io.print(" %r{} = call {{ i64, i1 }} @llvm.umul.with.overflow.i64(i64 %r{}, i64 {})\n", output, {pair, count, width})
|
||||
try io.print(" %r{} = extractvalue {{ i64, i1 }} %r{}, 1\n", output, {overflow, pair})
|
||||
try check_failure(state, overflow, .integer_overflow, instruction.span)
|
||||
try io.print(" %r{} = extractvalue {{ i64, i1 }} %r{}, 0\n", output, {size, pair})
|
||||
}
|
||||
nonempty :: fresh(state)
|
||||
destination_null :: fresh(state)
|
||||
try io.print(" %r{} = icmp ne i64 %r{}, 0\n", output, {nonempty, count})
|
||||
try io.print(" %r{} = icmp eq ptr %r{}, null\n", output, {destination_null, destination})
|
||||
missing := destination_null
|
||||
if (instruction.op == .memory_copy) {
|
||||
source_null :: fresh(state)
|
||||
missing = fresh(state)
|
||||
try io.print(" %r{} = icmp eq ptr %r{}, null\n", output, {source_null, operand})
|
||||
try io.print(" %r{} = or i1 %r{}, %r{}\n", output, {missing, destination_null, source_null})
|
||||
}
|
||||
invalid :: fresh(state)
|
||||
try io.print(" %r{} = and i1 %r{}, %r{}\n", output, {invalid, nonempty, missing})
|
||||
try check_failure(state, invalid, .invalid_pointer_access, instruction.span)
|
||||
if (instruction.op == .memory_copy) {
|
||||
destination_bits :: fresh(state)
|
||||
source_bits :: fresh(state)
|
||||
ordered :: fresh(state)
|
||||
forward :: fresh(state)
|
||||
backward :: fresh(state)
|
||||
distance :: fresh(state)
|
||||
overlap :: fresh(state)
|
||||
try io.print(" %r{} = ptrtoint ptr %r{} to i64\n", output, {destination_bits, destination})
|
||||
try io.print(" %r{} = ptrtoint ptr %r{} to i64\n", output, {source_bits, operand})
|
||||
try io.print(" %r{} = icmp uge i64 %r{}, %r{}\n", output, {ordered, destination_bits, source_bits})
|
||||
try io.print(" %r{} = sub i64 %r{}, %r{}\n", output, {forward, destination_bits, source_bits})
|
||||
try io.print(" %r{} = sub i64 %r{}, %r{}\n", output, {backward, source_bits, destination_bits})
|
||||
try io.print(" %r{} = select i1 %r{}, i64 %r{}, i64 %r{}\n", output, {distance, ordered, forward, backward})
|
||||
try io.print(" %r{} = icmp ult i64 %r{}, %r{}\n", output, {overlap, distance, size})
|
||||
try check_failure(state, overlap, .overlapping_memory_copy, instruction.span)
|
||||
try io.print(" call void @llvm.memcpy.p0.p0.i64(ptr %r{}, ptr %r{}, i64 %r{}, i1 false)\n", output, {destination, operand, size})
|
||||
return
|
||||
}
|
||||
label :: fresh(state)
|
||||
index :: fresh(state)
|
||||
next :: fresh(state)
|
||||
active :: fresh(state)
|
||||
pointer :: fresh(state)
|
||||
try io.print(" br label %set_start{}\nset_start{}:\n br label %set_test{}\nset_test{}:\n", output, {label, label, label, label})
|
||||
try io.print(" %r{} = phi i64 [ 0, %set_start{} ], [ %r{}, %set_body{} ]\n", output, {index, label, next, label})
|
||||
try io.print(" %r{} = icmp ult i64 %r{}, %r{}\n", output, {active, index, count})
|
||||
try io.print(" br i1 %r{}, label %set_body{}, label %set_end{}\nset_body{}:\n", output, {active, label, label, label})
|
||||
try io.print(" %r{} = getelementptr ", output, {pointer})
|
||||
try write_type(output, state.module, element)
|
||||
try io.print(", ptr %r{}, i64 %r{}\n", output, {destination, index})
|
||||
try store_payload(state, element, operand, pointer)
|
||||
try io.print(" %r{} = add i64 %r{}, 1\n br label %set_test{}\nset_end{}:\n", output, {next, index, label, label})
|
||||
}
|
||||
|
||||
@hide:file
|
||||
check_union proc(state @mut State, pointer usize, shape ir.ArrayType, field usize, span diagnostics.Span) void ! io.WriteError {
|
||||
if (shape.tag_type == .void) return
|
||||
@@ -351,7 +528,7 @@ store_payload proc(state @mut State, type Type, value, pointer usize) void ! io.
|
||||
}
|
||||
try io.write_all(state.output, " store ")
|
||||
try write_type(state.output, state.module, type)
|
||||
try io.print(" %r{}, ptr %r{}\n", state.output, {value, pointer})
|
||||
try io.print(" %r{}, ptr %r{}, align 1\n", state.output, {value, pointer})
|
||||
}
|
||||
|
||||
@hide:file
|
||||
|
||||
+106
-16
@@ -46,7 +46,7 @@ emit proc(
|
||||
try io.write_all(output, runtime)
|
||||
for (sources) |source, index| {
|
||||
try io.print("\n@p{} = private constant ", output, {index})
|
||||
try bytes_constant(output, source.path)
|
||||
try bytes_constant(output, source.path, false)
|
||||
}
|
||||
try io.print("\n@paths = private constant [{} x %string] [", output, {sources.len})
|
||||
for (sources) |source, index| {
|
||||
@@ -54,14 +54,23 @@ emit proc(
|
||||
try io.print("%string {{ ptr @p{}, i64 {} }}", output, {index, source.path.len})
|
||||
}
|
||||
try io.write_all(output, "]\n")
|
||||
for (module.strings.items) |_, index| {
|
||||
for (module.strings.items) |string, index| {
|
||||
try io.print("@s{} = private constant ", output, {index})
|
||||
try bytes_constant(output, ir.string_bytes(module, ir.StringId(index)))
|
||||
try bytes_constant(output, string.allocation[..string.len], true)
|
||||
try io.write_all(output, "\n")
|
||||
}
|
||||
for (diagnostic_store.list.items) |diagnostic, index| {
|
||||
try io.print("@d{} = private constant ", output, {index})
|
||||
try bytes_constant(output, enummap.get(diagnostics.Code, []u8, &diagnostics.messages, diagnostic.code)?)
|
||||
try bytes_constant(output, enummap.get(diagnostics.Code, []u8, &diagnostics.messages, diagnostic.code)?, false)
|
||||
try io.write_all(output, "\n")
|
||||
}
|
||||
memory_codes [6]diagnostics.Code :: [
|
||||
.null_unwrap, .invalid_slice_bounds, .sentinel_mismatch,
|
||||
.overlapping_memory_copy, .unsupported_foreign_call, .invalid_pointer_access,
|
||||
]
|
||||
for (memory_codes) |code| {
|
||||
try io.print("@memory_{s} = private constant ", output, {memory_symbol(code)})
|
||||
try bytes_constant(output, enummap.get(diagnostics.Code, []u8, &diagnostics.messages, code)?, false)
|
||||
try io.write_all(output, "\n")
|
||||
}
|
||||
try emit_reporting(output, diagnostic_store, line_starts.items, sources, primary)
|
||||
@@ -77,6 +86,14 @@ emit proc(
|
||||
else try emit_array_printer(output, module, ir.ArrayId(index))
|
||||
}
|
||||
for (module.functions.items) |function, index| {
|
||||
if (function.external_name.len != 0) {
|
||||
duplicate := false
|
||||
for (module.functions.items[..index]) |previous| {
|
||||
if (mem.eql(previous.external_name, function.external_name)) duplicate = true
|
||||
}
|
||||
if (duplicate or mem.eql(function.external_name, "write") or mem.eql(function.external_name, "__error")
|
||||
or mem.eql(function.external_name, "snprintf") or mem.eql(function.external_name, "exit")) continue
|
||||
}
|
||||
values :: try mem.alloc(Type, allocator, function.value_count)
|
||||
defer mem.free(allocator, values)
|
||||
mem.set(values, Type{})
|
||||
@@ -97,8 +114,10 @@ emit proc(
|
||||
if (result.kind != .void) try io.write_all(output, " %r0 = ")
|
||||
else try io.write_all(output, " ")
|
||||
try io.write_all(output, "call ")
|
||||
try write_type(output, module, result)
|
||||
try io.print(" @f{}()\n", output, {u32(entry)})
|
||||
try write_abi_result(output, module, result)
|
||||
try io.write_all(output, " ")
|
||||
try function_symbol(output, module, entry)
|
||||
try io.write_all(output, "()\n")
|
||||
if (result.kind != .void and result.kind != .invalid) {
|
||||
try print_call(output, module, result, 0)
|
||||
try io.write_all(output, " call void @out(i32 1, ptr @newline, i64 1)\n")
|
||||
@@ -113,13 +132,15 @@ emit proc(
|
||||
}
|
||||
|
||||
@hide:file
|
||||
bytes_constant proc(output io.Writer, bytes []u8) void ! io.WriteError {
|
||||
try io.print("[{} x i8] c\"", output, {bytes.len})
|
||||
bytes_constant proc(output io.Writer, bytes []u8, terminated bool) void ! io.WriteError {
|
||||
size usize :: if (terminated) bytes.len + 1 else bytes.len
|
||||
try io.print("[{} x i8] c\"", output, {size})
|
||||
digits :: "0123456789ABCDEF"
|
||||
for (bytes) |byte| {
|
||||
escaped [3]u8 :: ['\\', digits[usize(divtrunc!(byte, 16))], digits[usize(rem!(byte, 16))]]
|
||||
try io.write_all(output, (&escaped)[..])
|
||||
}
|
||||
if (terminated) try io.write_all(output, "\\00")
|
||||
try io.write_all(output, "\"")
|
||||
}
|
||||
|
||||
@@ -161,6 +182,45 @@ write_type proc(output io.Writer, module @ir.Module, type Type) void ! io.WriteE
|
||||
}
|
||||
}
|
||||
|
||||
@hide:file
|
||||
abi_extension proc(kind ir.Type) []u8 {
|
||||
return match kind {
|
||||
.i8, .i16: "signext"
|
||||
.u8, .u16, .bool: "zeroext"
|
||||
else: ""
|
||||
}
|
||||
}
|
||||
|
||||
@hide
|
||||
write_abi_result proc(output io.Writer, module @ir.Module, type Type) void ! io.WriteError {
|
||||
extension :: abi_extension(type.kind)
|
||||
if (extension.len != 0) try io.print("{s} ", output, {extension})
|
||||
try write_type(output, module, type)
|
||||
}
|
||||
|
||||
@hide
|
||||
write_abi_parameter proc(output io.Writer, module @ir.Module, type Type) void ! io.WriteError {
|
||||
try write_type(output, module, type)
|
||||
extension :: abi_extension(type.kind)
|
||||
if (extension.len != 0) try io.print(" {s}", output, {extension})
|
||||
}
|
||||
|
||||
@hide
|
||||
function_symbol proc(output io.Writer, module @ir.Module, id ir.FunctionId) void ! io.WriteError {
|
||||
name :: module.functions.items[usize(id)].external_name
|
||||
if (name.len == 0) {
|
||||
try io.print("@f{}", output, {u32(id)})
|
||||
return
|
||||
}
|
||||
try io.write_all(output, "@\"")
|
||||
digits :: "0123456789ABCDEF"
|
||||
for (name) |byte| {
|
||||
escaped [3]u8 :: ['\\', digits[usize(divtrunc!(byte, 16))], digits[usize(rem!(byte, 16))]]
|
||||
try io.write_all(output, (&escaped)[..])
|
||||
}
|
||||
try io.write_all(output, "\"")
|
||||
}
|
||||
|
||||
@hide:file
|
||||
local_type proc(function @ir.Function, index usize) Type {
|
||||
type := Type{ kind = function.locals.items[index] }
|
||||
@@ -252,7 +312,7 @@ emit_global proc(output io.Writer, module @ir.Module, global ir.Global, index us
|
||||
try io.print(" call void @f{}()\n", output, {u32(global.initializer)})
|
||||
} else {
|
||||
try io.write_all(output, " %value = call ")
|
||||
try write_type(output, module, type)
|
||||
try write_abi_result(output, module, type)
|
||||
try io.print(" @f{}()\n store ", output, {u32(global.initializer)})
|
||||
try write_type(output, module, type)
|
||||
try io.print(" %value, ptr @g{}\n", output, {index})
|
||||
@@ -406,6 +466,19 @@ check_local proc(state @State, slot u32, read bool, span diagnostics.Span) void
|
||||
try io.print(" call void @check_init(ptr %i{}, i1 {}, i64 {}, i64 {}, i64 {})\n", state.output, {slot, read, usize(span.source), position.line, position.column})
|
||||
}
|
||||
|
||||
@hide:file
|
||||
memory_symbol proc(code diagnostics.Code) []u8 {
|
||||
return match code {
|
||||
.null_unwrap: "null_unwrap"
|
||||
.invalid_slice_bounds: "invalid_slice_bounds"
|
||||
.sentinel_mismatch: "sentinel_mismatch"
|
||||
.overlapping_memory_copy: "overlapping_memory_copy"
|
||||
.unsupported_foreign_call: "unsupported_foreign_call"
|
||||
.invalid_pointer_access: "invalid_pointer_access"
|
||||
else: unreachable
|
||||
}
|
||||
}
|
||||
|
||||
@hide:file
|
||||
trap proc(state @State, code diagnostics.Code, span diagnostics.Span) void ! io.WriteError {
|
||||
position :: location(state.line_starts, span)
|
||||
@@ -413,11 +486,14 @@ trap proc(state @State, code diagnostics.Code, span diagnostics.Span) void ! io.
|
||||
.integer_overflow: "overflow"
|
||||
.index_out_of_bounds: "bounds"
|
||||
.inactive_union_field: "inactive_union"
|
||||
else: unreachable
|
||||
else: ""
|
||||
}
|
||||
message :: enummap.get(diagnostics.Code, []u8, &diagnostics.messages, code)?
|
||||
length :: message.len
|
||||
try io.print(" call void @trap(i64 -1, i64 {}, i64 {}, i64 {}, ptr @{s}, i64 {})\n unreachable\n", state.output, {usize(span.source), position.line, position.column, symbol, length})
|
||||
try io.print(" call void @trap(i64 -1, i64 {}, i64 {}, i64 {}, ptr @", state.output, {usize(span.source), position.line, position.column})
|
||||
if (symbol.len != 0) try io.write_all(state.output, symbol)
|
||||
else try io.print("memory_{s}", state.output, {memory_symbol(code)})
|
||||
try io.print(", i64 {})\n unreachable\n", state.output, {length})
|
||||
}
|
||||
|
||||
@hide
|
||||
@@ -439,7 +515,14 @@ write_constant proc(output io.Writer, module @ir.Module, value ir.Value) void !
|
||||
}
|
||||
.bool: try io.print("{}", output, {value.data.boolean})
|
||||
.string: try io.print("{{ ptr @s{}, i64 {} }}", output, {u32(value.data.string), ir.string_bytes(module, value.data.string).len})
|
||||
.function: try io.print("@f{}", output, {u32(value.data.function)})
|
||||
.pointer: {
|
||||
if (value.data.unsigned == maxval!(u64)) try io.write_all(output, "null")
|
||||
else unreachable
|
||||
}
|
||||
.function: {
|
||||
if (value.data.function == maxval!(ir.FunctionId)) try io.write_all(output, "null")
|
||||
else try function_symbol(output, module, value.data.function)
|
||||
}
|
||||
.array: {
|
||||
shape :: module.arrays.items[usize(value.array_type)]
|
||||
try io.write_all(output, "[")
|
||||
@@ -482,14 +565,21 @@ write_constant proc(output io.Writer, module @ir.Module, value ir.Value) void !
|
||||
@hide:file
|
||||
emit_function proc(state @mut State, function @ir.Function, id usize) void ! io.WriteError {
|
||||
output :: state.output
|
||||
try io.write_all(output, "define internal ")
|
||||
try write_type(output, state.module, Type{ kind = function.result, shape = function.result_array_type })
|
||||
try io.print(" @f{}(", output, {id})
|
||||
if (function.external_name.len != 0) try io.write_all(output, "declare ")
|
||||
else try io.write_all(output, "define internal ")
|
||||
try write_abi_result(output, state.module, Type{ kind = function.result, shape = function.result_array_type })
|
||||
try io.write_all(output, " ")
|
||||
try function_symbol(output, state.module, ir.FunctionId(id))
|
||||
try io.write_all(output, "(")
|
||||
for (function.parameters.items) |_, index| {
|
||||
if (index != 0) try io.write_all(output, ", ")
|
||||
try write_type(output, state.module, local_type(function, index))
|
||||
try write_abi_parameter(output, state.module, local_type(function, index))
|
||||
try io.print(" %p{}", output, {index})
|
||||
}
|
||||
if (function.external_name.len != 0) {
|
||||
try io.write_all(output, ")\n")
|
||||
return
|
||||
}
|
||||
try io.write_all(output, ") {\nentry:\n")
|
||||
for (function.locals.items) |_, index| {
|
||||
type :: local_type(function, index)
|
||||
|
||||
@@ -8,6 +8,7 @@ runtime ::
|
||||
`declare i32 @snprintf(ptr, i64, ptr, ...)
|
||||
`declare void @exit(i32) noreturn
|
||||
`declare double @llvm.trunc.f64(double)
|
||||
`declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1 immarg)
|
||||
`declare { i8, i1 } @llvm.sadd.with.overflow.i8(i8, i8)
|
||||
`declare { i8, i1 } @llvm.ssub.with.overflow.i8(i8, i8)
|
||||
`declare { i8, i1 } @llvm.smul.with.overflow.i8(i8, i8)
|
||||
|
||||
Reference in New Issue
Block a user