enforce integer division via explicit builtins
This commit is contained in:
+222
-40
@@ -257,7 +257,10 @@ valid_value :: proc(
|
||||
.Fallible_Error, .Extract, .Select, .Unwrap,
|
||||
.Optional_Is_Some, .Optional_Value, .Orelse,
|
||||
.Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
|
||||
.Neg_Checked, .Add_Checked, .Sub_Checked, .Mul_Checked, .Div_Checked, .Pointer_Add, .Not, .Compare, .Call:
|
||||
.Neg_Checked, .Add_Checked, .Sub_Checked, .Mul_Checked, .Div_Checked,
|
||||
.Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
|
||||
.Rem_Checked, .Mod_Checked,
|
||||
.Pointer_Add, .Not, .Compare, .Call:
|
||||
return true
|
||||
case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin,
|
||||
.Store, .Fill, .Trap, .Label, .Br, .Cond_Br, .Return, .Return_Void:
|
||||
@@ -582,9 +585,7 @@ emit_checked_arithmetic :: proc(
|
||||
fmt.sbprintf(&emitter.builder, " unreachable\noverflow_continue%d:\n", instruction_index)
|
||||
}
|
||||
|
||||
// emit_checked_division emits a trapping integer division guarding divide-by-zero
|
||||
// and signed `INT_MIN / -1` overflow, or a plain floating-point division.
|
||||
emit_checked_division :: proc(
|
||||
emit_division_zero_guard :: proc(
|
||||
emitter: ^Emitter,
|
||||
instructions: []ir.Instruction,
|
||||
instruction_index: int,
|
||||
@@ -592,56 +593,225 @@ emit_checked_division :: proc(
|
||||
) {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
if types.is_float(instruction.type, emitter.module.target) {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = fdiv %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
fmt.sbprintf(&emitter.builder, " %%divzero%d = fcmp oeq %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
return
|
||||
strings.write_string(&emitter.builder, ", 0.000000e+00\n")
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, " %%divzero%d = icmp eq %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", 0\n")
|
||||
}
|
||||
signed := !types.is_unsigned(instruction.type, emitter.module.target)
|
||||
fmt.sbprintf(&emitter.builder, " %%divzero%d = icmp eq %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", 0\n")
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" br i1 %%divzero%d, label %%divzero_trap%d, label %%divzero_ok%d\n",
|
||||
" br i1 %%divzero%d, label %%divzero_trap%d, label %%divzero_ok%d\ndivzero_trap%d:\n",
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
)
|
||||
fmt.sbprintf(&emitter.builder, "divzero_trap%d:\n", instruction_index)
|
||||
zero_message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "integer division by zero")
|
||||
emit_trap_call(emitter, zero_message)
|
||||
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "division builtin denominator is zero")
|
||||
emit_trap_call(emitter, message)
|
||||
fmt.sbprintf(&emitter.builder, " unreachable\ndivzero_ok%d:\n", instruction_index)
|
||||
if signed {
|
||||
min_value := -(i128(1) << u32(types.bits(instruction.type, emitter.module.target) - 1))
|
||||
fmt.sbprintf(&emitter.builder, " %%divminlo%d = icmp eq %s ", instruction_index, type_name)
|
||||
}
|
||||
|
||||
emit_division_overflow_guard :: proc(
|
||||
emitter: ^Emitter,
|
||||
instructions: []ir.Instruction,
|
||||
instruction_index: int,
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
min_value := -(i128(1) << u32(types.bits(instruction.type, emitter.module.target)-1))
|
||||
fmt.sbprintf(&emitter.builder, " %%divminlo%d = icmp eq %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", %d\n %%divminhi%d = icmp eq %s ", min_value, instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
", -1\n %%divovf%d = and i1 %%divminlo%d, %%divminhi%d\n br i1 %%divovf%d, label %%divovf_trap%d, label %%divovf_ok%d\ndivovf_trap%d:\n",
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
)
|
||||
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "signed integer division overflow")
|
||||
emit_trap_call(emitter, message)
|
||||
fmt.sbprintf(&emitter.builder, " unreachable\ndivovf_ok%d:\n", instruction_index)
|
||||
}
|
||||
|
||||
emit_float_division_builtin :: proc(
|
||||
emitter: ^Emitter,
|
||||
instructions: []ir.Instruction,
|
||||
instruction_index: int,
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
suffix := "f32" if types.bits(instruction.type, emitter.module.target) == 32 else "f64"
|
||||
|
||||
if instruction.op == .Rem_Checked || instruction.op == .Mod_Checked {
|
||||
name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Rem_Checked else fmt.tprintf("%%rawrem%d", instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %s = frem %s ", name, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", %d\n", min_value)
|
||||
fmt.sbprintf(&emitter.builder, " %%divminhi%d = icmp eq %s ", instruction_index, type_name)
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", -1\n")
|
||||
fmt.sbprintf(&emitter.builder, " %%divovf%d = and i1 %%divminlo%d, %%divminhi%d\n", instruction_index, instruction_index, instruction_index)
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" br i1 %%divovf%d, label %%divovf_trap%d, label %%divovf_ok%d\n",
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
instruction_index,
|
||||
)
|
||||
fmt.sbprintf(&emitter.builder, "divovf_trap%d:\n", instruction_index)
|
||||
ovf_message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "signed integer division overflow")
|
||||
emit_trap_call(emitter, ovf_message)
|
||||
fmt.sbprintf(&emitter.builder, " unreachable\ndivovf_ok%d:\n", instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = sdiv %s ", instruction_index, type_name)
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = udiv %s ", instruction_index, type_name)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
if instruction.op == .Rem_Checked {
|
||||
return
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%remnonzero%d = fcmp one %s %%rawrem%d, 0.000000e+00\n", instruction_index, type_name, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%remsign%d = fcmp olt %s %%rawrem%d, 0.000000e+00\n", instruction_index, type_name, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%denomsign%d = fcmp olt %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", 0.000000e+00\n")
|
||||
fmt.sbprintf(&emitter.builder, " %%signsdiffer%d = xor i1 %%remsign%d, %%denomsign%d\n", instruction_index, instruction_index, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%modadjust%d = and i1 %%remnonzero%d, %%signsdiffer%d\n", instruction_index, instruction_index, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%adjustedrem%d = fadd %s %%rawrem%d, ", instruction_index, type_name, instruction_index)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, "\n %%v%d = select i1 %%modadjust%d, %s %%adjustedrem%d, %s %%rawrem%d\n", instruction_index, instruction_index, type_name, instruction_index, type_name, instruction_index)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.sbprintf(&emitter.builder, " %%divq%d = fdiv %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
intrinsic := "trunc"
|
||||
if instruction.op == .Div_Floor_Checked {
|
||||
intrinsic = "floor"
|
||||
} else if instruction.op == .Div_Ceil_Checked {
|
||||
intrinsic = "ceil"
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = call %s @llvm.%s.%s(%s %%divq%d)\n", instruction_index, type_name, intrinsic, suffix, type_name, instruction_index)
|
||||
if instruction.op != .Div_Exact_Checked {
|
||||
return
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%exactprod%d = fmul %s %%v%d, ", instruction_index, type_name, instruction_index)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, "\n %%exact%d = fcmp oeq %s %%exactprod%d, ", instruction_index, type_name, instruction_index)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, "\n br i1 %%exact%d, label %%exact_ok%d, label %%exact_trap%d\nexact_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "exact division has a remainder")
|
||||
emit_trap_call(emitter, message)
|
||||
fmt.sbprintf(&emitter.builder, " unreachable\nexact_ok%d:\n", instruction_index)
|
||||
}
|
||||
|
||||
emit_integer_remainder_builtin :: proc(
|
||||
emitter: ^Emitter,
|
||||
instructions: []ir.Instruction,
|
||||
instruction_index: int,
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
signed := types.is_signed(instruction.type, emitter.module.target)
|
||||
raw_name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Rem_Checked || !signed else fmt.tprintf("%%rawrem%d", instruction_index)
|
||||
if !signed {
|
||||
fmt.sbprintf(&emitter.builder, " %s = urem %s ", raw_name, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
} else {
|
||||
min_value := -(i128(1) << u32(types.bits(instruction.type, emitter.module.target)-1))
|
||||
fmt.sbprintf(&emitter.builder, " %%remminlo%d = icmp eq %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", %d\n %%remminhi%d = icmp eq %s ", min_value, instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", -1\n %%remspecial%d = and i1 %%remminlo%d, %%remminhi%d\n", instruction_index, instruction_index, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " br i1 %%remspecial%d, label %%rem_special%d, label %%rem_normal%d\nrem_special%d:\n br label %%rem_join%d\nrem_normal%d:\n", instruction_index, instruction_index, instruction_index, instruction_index, instruction_index, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%remnormal%d = srem %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, "\n br label %%rem_join%d\nrem_join%d:\n %s = phi %s [ 0, %%rem_special%d ], [ %%remnormal%d, %%rem_normal%d ]\n", instruction_index, instruction_index, raw_name, type_name, instruction_index, instruction_index, instruction_index)
|
||||
}
|
||||
if instruction.op == .Rem_Checked || !signed {
|
||||
return
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%remnonzero%d = icmp ne %s %%rawrem%d, 0\n", instruction_index, type_name, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%remsign%d = icmp slt %s %%rawrem%d, 0\n", instruction_index, type_name, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%denomsign%d = icmp slt %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", 0\n %%signsdiffer%d = xor i1 %%remsign%d, %%denomsign%d\n", instruction_index, instruction_index, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%modadjust%d = and i1 %%remnonzero%d, %%signsdiffer%d\n %%adjustedrem%d = add %s %%rawrem%d, ", instruction_index, instruction_index, instruction_index, instruction_index, type_name, instruction_index)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, "\n %%v%d = select i1 %%modadjust%d, %s %%adjustedrem%d, %s %%rawrem%d\n", instruction_index, instruction_index, type_name, instruction_index, type_name, instruction_index)
|
||||
}
|
||||
|
||||
emit_integer_quotient_builtin :: proc(
|
||||
emitter: ^Emitter,
|
||||
instructions: []ir.Instruction,
|
||||
instruction_index: int,
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
signed := types.is_signed(instruction.type, emitter.module.target)
|
||||
operation := "sdiv" if signed else "udiv"
|
||||
name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Div_Trunc_Checked else fmt.tprintf("%%divq%d", instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %s = %s %s ", name, operation, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
if instruction.op == .Div_Trunc_Checked {
|
||||
return
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%divprod%d = mul %s %%divq%d, ", instruction_index, type_name, instruction_index)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, "\n %%divrem%d = sub %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", %%divprod%d\n", instruction_index)
|
||||
if instruction.op == .Div_Exact_Checked {
|
||||
fmt.sbprintf(&emitter.builder, " %%exact%d = icmp eq %s %%divrem%d, 0\n br i1 %%exact%d, label %%exact_ok%d, label %%exact_trap%d\nexact_trap%d:\n", instruction_index, type_name, instruction_index, instruction_index, instruction_index, instruction_index, instruction_index)
|
||||
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "exact division has a remainder")
|
||||
emit_trap_call(emitter, message)
|
||||
fmt.sbprintf(&emitter.builder, " unreachable\nexact_ok%d:\n %%v%d = add %s %%divq%d, 0\n", instruction_index, instruction_index, type_name, instruction_index)
|
||||
return
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%remnonzero%d = icmp ne %s %%divrem%d, 0\n", instruction_index, type_name, instruction_index)
|
||||
if signed {
|
||||
fmt.sbprintf(&emitter.builder, " %%numsign%d = icmp slt %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", 0\n %%denomsign%d = icmp slt %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", 0\n %%signsdiffer%d = xor i1 %%numsign%d, %%denomsign%d\n", instruction_index, instruction_index, instruction_index)
|
||||
predicate := fmt.tprintf("%%signsdiffer%d", instruction_index)
|
||||
if instruction.op == .Div_Ceil_Checked {
|
||||
fmt.sbprintf(&emitter.builder, " %%signssame%d = xor i1 %%signsdiffer%d, true\n", instruction_index, instruction_index)
|
||||
predicate = fmt.tprintf("%%signssame%d", instruction_index)
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%divadjust%d = and i1 %%remnonzero%d, %s\n", instruction_index, instruction_index, predicate)
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, " %%divadjust%d = and i1 %%remnonzero%d, true\n", instruction_index, instruction_index)
|
||||
}
|
||||
adjustment := "sub" if instruction.op == .Div_Floor_Checked else "add"
|
||||
fmt.sbprintf(&emitter.builder, " %%adjustedq%d = %s %s %%divq%d, 1\n %%v%d = select i1 %%divadjust%d, %s %%adjustedq%d, %s %%divq%d\n", instruction_index, adjustment, type_name, instruction_index, instruction_index, instruction_index, type_name, instruction_index, type_name, instruction_index)
|
||||
}
|
||||
|
||||
emit_division_builtin :: proc(
|
||||
emitter: ^Emitter,
|
||||
instructions: []ir.Instruction,
|
||||
instruction_index: int,
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
emit_division_zero_guard(emitter, instructions, instruction_index, instruction)
|
||||
if types.is_float(instruction.type, emitter.module.target) {
|
||||
emit_float_division_builtin(emitter, instructions, instruction_index, instruction)
|
||||
return
|
||||
}
|
||||
quotient := instruction.op == .Div_Trunc_Checked || instruction.op == .Div_Floor_Checked ||
|
||||
instruction.op == .Div_Exact_Checked || instruction.op == .Div_Ceil_Checked
|
||||
if quotient && types.is_signed(instruction.type, emitter.module.target) {
|
||||
emit_division_overflow_guard(emitter, instructions, instruction_index, instruction)
|
||||
}
|
||||
if quotient {
|
||||
emit_integer_quotient_builtin(emitter, instructions, instruction_index, instruction)
|
||||
} else {
|
||||
emit_integer_remainder_builtin(emitter, instructions, instruction_index, instruction)
|
||||
}
|
||||
}
|
||||
|
||||
emit_instruction_stream :: proc(
|
||||
@@ -1609,11 +1779,22 @@ emit_instruction_stream :: proc(
|
||||
emit_checked_arithmetic(emitter, instructions, instruction_index, instruction, "mul", "fmul", "integer multiplication overflow")
|
||||
case .Div_Checked:
|
||||
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) {
|
||||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) ||
|
||||
!types.is_float(instruction.type, emitter.module.target) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid division operand")
|
||||
continue
|
||||
}
|
||||
emit_checked_division(emitter, instructions, instruction_index, instruction)
|
||||
emit_checked_arithmetic(emitter, instructions, instruction_index, instruction, "div", "fdiv", "")
|
||||
case .Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
|
||||
.Rem_Checked, .Mod_Checked:
|
||||
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) ||
|
||||
(!types.is_concrete_integer(instruction.type) &&
|
||||
!types.is_float(instruction.type, emitter.module.target)) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid division builtin operands")
|
||||
continue
|
||||
}
|
||||
emit_division_builtin(emitter, instructions, instruction_index, instruction)
|
||||
case .Pointer_Add:
|
||||
result_item, result_ok := types.node(&emitter.module.types, instruction.type)
|
||||
base_type := instructions[instruction.a].type if valid_instruction(instructions, instruction.a) else types.INVALID
|
||||
@@ -2283,6 +2464,7 @@ emit_messages :: proc(emitter: ^Emitter) {
|
||||
|
||||
emit_declarations :: proc(emitter: ^Emitter) {
|
||||
strings.write_string(&emitter.builder, "declare i64 @write(i32, ptr, i64)\ndeclare void @llvm.trap()\ndeclare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1 immarg)\ndeclare void @llvm.memset.p0.i64(ptr, i8, i64, i1 immarg)\n")
|
||||
strings.write_string(&emitter.builder, "declare float @llvm.trunc.f32(float)\ndeclare double @llvm.trunc.f64(double)\ndeclare float @llvm.floor.f32(float)\ndeclare double @llvm.floor.f64(double)\ndeclare float @llvm.ceil.f32(float)\ndeclare double @llvm.ceil.f64(double)\n")
|
||||
widths := [?]int{8, 16, 32, 64}
|
||||
overflow_intrinsics := [?]string{"sadd", "uadd", "ssub", "usub", "smul", "umul"}
|
||||
for bits in widths {
|
||||
|
||||
Reference in New Issue
Block a user