Files
honey/source/llvm/instructions.hon
T

222 lines
12 KiB
Plaintext

import "@std/io"
import "@source/ir"
@hide
emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruction) void ! io.WriteError {
output :: state.output
type :: Type{ kind = instruction.type, shape = instruction.array_type }
result := fresh(state)
match instruction.op {
.constant: {
if (instruction.type == .void) return
try io.write_all(output, " store ")
try write_type(output, state.module, type)
try io.write_all(output, " ")
try write_constant(output, state.module, instruction.constant)
try io.print(", ptr %v{}\n", output, {u32(instruction.destination)})
return
}
.load_local: {
try check_local(state, instruction.slot, true, instruction.span)
try io.print(" %r{} = load ", output, {result})
try write_type(output, state.module, type)
try io.print(", ptr %l{}\n", output, {instruction.slot})
}
.store_local, .assign_local: {
if (instruction.op == .assign_local) try check_local(state, instruction.slot, false, instruction.span)
value :: try load(state, instruction.left)
try io.write_all(output, " store ")
try write_type(output, state.module, state.values[usize(instruction.left)])
try io.print(" %r{}, ptr %l{}\n", output, {value, instruction.slot})
if (instruction.op == .store_local) try io.print(" store i1 true, ptr %i{}\n", output, {instruction.slot})
return
}
.reset_local: {
try io.print(" store i1 false, ptr %i{}\n", output, {instruction.slot})
return
}
.load_global: {
pointer :: fresh(state)
position :: location(state.line_starts, instruction.span)
try io.print(" %r{} = call ptr @global{}(i64 {}, i64 {})\n", output, {pointer, instruction.slot, position.line, position.column})
try io.print(" %r{} = load ", output, {result})
try write_type(output, state.module, type)
try io.print(", ptr %r{}\n", output, {pointer})
}
.array_literal: {
for (0..instruction.arguments_count) |index| {
operand :: block.arguments.items[instruction.arguments_start + index]
value :: try load(state, operand)
pointer :: fresh(state)
try io.print(" %r{} = getelementptr ", output, {pointer})
try write_type(output, state.module, type)
try io.print(", ptr %v{}, i64 0, i64 {}\n store ", output, {u32(instruction.destination), index})
try write_type(output, state.module, state.values[usize(operand)])
try io.print(" %r{}, ptr %r{}\n", output, {value, pointer})
}
return
}
.index, .index_address: {
index :: try load(state, instruction.right)
base_type :: state.values[usize(instruction.left)]
shape :: state.module.arrays.items[usize(base_type.shape)]
failed :: fresh(state)
try io.print(" %r{} = icmp uge i64 %r{}, {}\n", output, {failed, index, shape.len})
try check_failure(state, failed, .index_out_of_bounds, instruction.span)
pointer :: fresh(state)
if (instruction.op == .index_address) {
base :: try load(state, instruction.left)
data :: fresh(state)
flag :: fresh(state)
address :: fresh(state)
try io.print(" %r{} = extractvalue %address %r{}, 0\n", output, {data, base})
try io.print(" %r{} = extractvalue %address %r{}, 1\n", output, {flag, base})
try io.print(" %r{} = getelementptr ", output, {pointer})
try write_type(output, state.module, Type{ kind = .array, shape = base_type.shape })
try io.print(", ptr %r{}, i64 0, i64 %r{}\n", output, {data, index})
try io.print(" %r{} = insertvalue %address poison, ptr %r{}, 0\n", output, {address, pointer})
try io.print(" %r{} = insertvalue %address %r{}, ptr %r{}, 1\n", output, {result, address, flag})
} else {
try io.print(" %r{} = getelementptr ", output, {pointer})
try write_type(output, state.module, base_type)
try io.print(", ptr %v{}, i64 0, i64 %r{}\n %r{} = load ", output, {u32(instruction.left), index, result})
try write_type(output, state.module, type)
try io.print(", ptr %r{}\n", output, {pointer})
}
}
.address_local: {
address :: fresh(state)
try io.print(" %r{} = insertvalue %address poison, ptr %l{}, 0\n", output, {address, instruction.slot})
try io.print(" %r{} = insertvalue %address %r{}, ptr %i{}, 1\n", output, {result, address, instruction.slot})
}
.address_global: {
pointer :: fresh(state)
address :: fresh(state)
position :: location(state.line_starts, instruction.span)
try io.print(" %r{} = call ptr @global{}(i64 {}, i64 {})\n", output, {pointer, instruction.slot, position.line, position.column})
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})
}
.load_address, .store_address: {
address :: try load(state, instruction.left)
pointer :: fresh(state)
flag :: fresh(state)
position :: location(state.line_starts, instruction.span)
try io.print(" %r{} = extractvalue %address %r{}, 0\n", output, {pointer, address})
try io.print(" %r{} = extractvalue %address %r{}, 1\n", output, {flag, address})
try io.print(" call void @check_init(ptr %r{}, i1 {}, i64 {}, i64 {})\n", output, {flag, instruction.op == .load_address, position.line, position.column})
if (instruction.op == .store_address) {
value :: try load(state, instruction.right)
try io.write_all(output, " store ")
try write_type(output, state.module, state.values[usize(instruction.right)])
try io.print(" %r{}, ptr %r{}\n", output, {value, pointer})
return
}
try io.print(" %r{} = load ", output, {result})
try write_type(output, state.module, type)
try io.print(", ptr %r{}\n", output, {pointer})
}
.call: {
callee :: try load(state, instruction.left)
# operands are frame snapshots; loading them before the call preserves evaluation order.
# argument registers are consecutive because load emits exactly one fresh register.
first_argument :: state.next_register
for (0..instruction.arguments_count) |index| {
_ = try load(state, block.arguments.items[instruction.arguments_start + index])
}
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 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 io.print(" %r{}", output, {first_argument + index})
}
try io.write_all(output, ")\n")
}
.negate: {
operand :: try load(state, instruction.left)
if (instruction.type == .float) {
try io.print(" %r{} = fneg double %r{}\n", output, {result, operand})
} else {
pair :: fresh(state)
overflow :: fresh(state)
try io.print(" %r{} = call {{ i64, i1 }} @llvm.ssub.with.overflow.i64(i64 0, i64 %r{})\n", output, {pair, operand})
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, {result, pair})
}
}
.logical_not: {
operand :: try load(state, instruction.left)
try io.print(" %r{} = xor i1 %r{}, true\n", output, {result, operand})
}
.add, .subtract, .multiply, .divide: {
left :: try load(state, instruction.left)
right :: try load(state, instruction.right)
if (instruction.type == .float) {
opcode []u8 :: match instruction.op {
.add: "fadd"
.subtract: "fsub"
.multiply: "fmul"
.divide: "fdiv"
else: unreachable
}
try io.print(" %r{} = {s} double %r{}, %r{}\n", output, {result, opcode, left, right})
} else {
opcode []u8 :: match instruction.op {
.add: "add"
.subtract: "sub"
.multiply: "mul"
else: unreachable
}
sign []u8 :: if (instruction.type == .usize) "u" else "s"
pair :: fresh(state)
overflow :: fresh(state)
try io.print(" %r{} = call {{ i64, i1 }} @llvm.{s}{s}.with.overflow.i64(i64 %r{}, i64 %r{})\n", output, {pair, sign, opcode, left, right})
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, {result, pair})
}
}
.equal, .not_equal, .less, .less_equal, .greater, .greater_equal: {
left :: try load(state, instruction.left)
right :: try load(state, instruction.right)
operand_type :: state.values[usize(instruction.left)]
if (operand_type.kind == .float) {
predicate []u8 :: match instruction.op {
.equal: "oeq"
.not_equal: "une"
.less: "olt"
.less_equal: "ole"
.greater: "ogt"
.greater_equal: "oge"
else: unreachable
}
try io.print(" %r{} = fcmp {s} double %r{}, %r{}\n", output, {result, predicate, left, right})
} else {
prefix []u8 := ""
if (instruction.op != .equal and instruction.op != .not_equal) {
if (operand_type.kind == .usize) prefix = "u"
else prefix = "s"
}
predicate []u8 :: match instruction.op {
.equal: "eq"
.not_equal: "ne"
.less: "lt"
.less_equal: "le"
.greater: "gt"
.greater_equal: "ge"
else: unreachable
}
try io.print(" %r{} = icmp {s}{s} ", output, {result, prefix, predicate})
try write_type(output, state.module, operand_type)
try io.print(" %r{}, %r{}\n", output, {left, right})
}
}
}
try store(state, instruction.destination, result)
}