Commit 3f3630d7e3
Changed files (7)
src/all_types.hpp
@@ -1187,6 +1187,8 @@ struct CodeGen {
LLVMValueRef memcpy_fn_val;
LLVMValueRef memset_fn_val;
LLVMValueRef trap_fn_val;
+ LLVMValueRef return_address_fn_val;
+ LLVMValueRef frame_address_fn_val;
bool error_during_imports;
uint32_t next_node_index;
TypeTableEntry *err_tag_type;
@@ -1420,6 +1422,10 @@ enum IrInstructionId {
IrInstructionIdSlice,
IrInstructionIdMemberCount,
IrInstructionIdBreakpoint,
+ IrInstructionIdReturnAddress,
+ IrInstructionIdFrameAddress,
+ IrInstructionIdAlignOf,
+ IrInstructionIdOverflowOp,
};
struct IrInstruction {
@@ -1965,6 +1971,39 @@ struct IrInstructionBreakpoint {
IrInstruction base;
};
+struct IrInstructionReturnAddress {
+ IrInstruction base;
+};
+
+struct IrInstructionFrameAddress {
+ IrInstruction base;
+};
+
+enum IrOverflowOp {
+ IrOverflowOpAdd,
+ IrOverflowOpSub,
+ IrOverflowOpMul,
+ IrOverflowOpShl,
+};
+
+struct IrInstructionOverflowOp {
+ IrInstruction base;
+
+ IrOverflowOp op;
+ IrInstruction *type_value;
+ IrInstruction *op1;
+ IrInstruction *op2;
+ IrInstruction *result_ptr;
+
+ TypeTableEntry *result_ptr_type;
+};
+
+struct IrInstructionAlignOf {
+ IrInstruction base;
+
+ IrInstruction *type_value;
+};
+
enum LValPurpose {
LValPurposeNone,
LValPurposeAssign,
src/analyze.cpp
@@ -876,7 +876,7 @@ static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *nod
size_t backward_branch_count = 0;
return ir_eval_const_value(g, scope, node, type_entry,
&backward_branch_count, default_backward_branch_quota,
- nullptr, nullptr);
+ nullptr, nullptr, node);
}
TypeTableEntry *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
src/codegen.cpp
@@ -2058,6 +2058,80 @@ static LLVMValueRef ir_render_breakpoint(CodeGen *g, IrExecutable *executable, I
return nullptr;
}
+static LLVMValueRef ir_render_return_address(CodeGen *g, IrExecutable *executable,
+ IrInstructionReturnAddress *instruction)
+{
+ LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref);
+ return LLVMBuildCall(g->builder, g->return_address_fn_val, &zero, 1, "");
+}
+
+static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable,
+ IrInstructionFrameAddress *instruction)
+{
+ LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref);
+ return LLVMBuildCall(g->builder, g->frame_address_fn_val, &zero, 1, "");
+}
+
+static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) {
+ TypeTableEntry *int_type = get_underlying_type(instruction->result_ptr_type);
+ assert(int_type->id == TypeTableEntryIdInt);
+
+ LLVMValueRef op1 = ir_llvm_value(g, instruction->op1);
+ LLVMValueRef op2 = ir_llvm_value(g, instruction->op2);
+ LLVMValueRef ptr_result = ir_llvm_value(g, instruction->result_ptr);
+
+ LLVMValueRef result = LLVMBuildShl(g->builder, op1, op2, "");
+ LLVMValueRef orig_val;
+ if (int_type->data.integral.is_signed) {
+ orig_val = LLVMBuildAShr(g->builder, result, op2, "");
+ } else {
+ orig_val = LLVMBuildLShr(g->builder, result, op2, "");
+ }
+ LLVMValueRef overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, op1, orig_val, "");
+
+ LLVMBuildStore(g->builder, result, ptr_result);
+
+ return overflow_bit;
+}
+
+static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutable *executable, IrInstructionOverflowOp *instruction) {
+ AddSubMul add_sub_mul;
+ switch (instruction->op) {
+ case IrOverflowOpAdd:
+ add_sub_mul = AddSubMulAdd;
+ break;
+ case IrOverflowOpSub:
+ add_sub_mul = AddSubMulAdd;
+ break;
+ case IrOverflowOpMul:
+ add_sub_mul = AddSubMulAdd;
+ break;
+ case IrOverflowOpShl:
+ return render_shl_with_overflow(g, instruction);
+ }
+
+ TypeTableEntry *int_type = get_underlying_type(instruction->result_ptr_type);
+ assert(int_type->id == TypeTableEntryIdInt);
+
+ LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul);
+
+ LLVMValueRef op1 = ir_llvm_value(g, instruction->op1);
+ LLVMValueRef op2 = ir_llvm_value(g, instruction->op2);
+ LLVMValueRef ptr_result = ir_llvm_value(g, instruction->result_ptr);
+
+ LLVMValueRef params[] = {
+ op1,
+ op2,
+ };
+
+ LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
+ LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
+ LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
+ LLVMBuildStore(g->builder, result, ptr_result);
+
+ return overflow_bit;
+}
+
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
AstNode *source_node = instruction->source_node;
Scope *scope = instruction->scope;
@@ -2100,6 +2174,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdEmbedFile:
case IrInstructionIdIntType:
case IrInstructionIdMemberCount:
+ case IrInstructionIdAlignOf:
zig_unreachable();
case IrInstructionIdReturn:
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -2169,6 +2244,12 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_slice(g, executable, (IrInstructionSlice *)instruction);
case IrInstructionIdBreakpoint:
return ir_render_breakpoint(g, executable, (IrInstructionBreakpoint *)instruction);
+ case IrInstructionIdReturnAddress:
+ return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction);
+ case IrInstructionIdFrameAddress:
+ return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction);
+ case IrInstructionIdOverflowOp:
+ return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);
case IrInstructionIdSwitchVar:
case IrInstructionIdContainerInitList:
case IrInstructionIdStructInit:
@@ -3300,6 +3381,8 @@ static void define_builtin_fns(CodeGen *g) {
&g->builtin_types.entry_i32->type_ref, 1, false);
builtin_fn->fn_val = LLVMAddFunction(g->module, "llvm.returnaddress", fn_type);
assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
+
+ g->return_address_fn_val = builtin_fn->fn_val;
}
{
BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdFrameAddress,
@@ -3310,6 +3393,8 @@ static void define_builtin_fns(CodeGen *g) {
&g->builtin_types.entry_i32->type_ref, 1, false);
builtin_fn->fn_val = LLVMAddFunction(g->module, "llvm.frameaddress", fn_type);
assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
+
+ g->frame_address_fn_val = builtin_fn->fn_val;
}
{
BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3);
src/ir.cpp
@@ -395,6 +395,22 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBreakpoint *) {
return IrInstructionIdBreakpoint;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionReturnAddress *) {
+ return IrInstructionIdReturnAddress;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *) {
+ return IrInstructionIdFrameAddress;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
+ return IrInstructionIdAlignOf;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionOverflowOp *) {
+ return IrInstructionIdOverflowOp;
+}
+
template<typename T>
static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate<T>(1);
@@ -1632,6 +1648,67 @@ static IrInstruction *ir_build_breakpoint_from(IrBuilder *irb, IrInstruction *ol
return new_instruction;
}
+static IrInstruction *ir_build_return_address(IrBuilder *irb, Scope *scope, AstNode *source_node) {
+ IrInstructionReturnAddress *instruction = ir_build_instruction<IrInstructionReturnAddress>(irb, scope, source_node);
+ return &instruction->base;
+}
+
+static IrInstruction *ir_build_return_address_from(IrBuilder *irb, IrInstruction *old_instruction) {
+ IrInstruction *new_instruction = ir_build_return_address(irb, old_instruction->scope, old_instruction->source_node);
+ ir_link_new_instruction(new_instruction, old_instruction);
+ return new_instruction;
+}
+
+static IrInstruction *ir_build_frame_address(IrBuilder *irb, Scope *scope, AstNode *source_node) {
+ IrInstructionFrameAddress *instruction = ir_build_instruction<IrInstructionFrameAddress>(irb, scope, source_node);
+ return &instruction->base;
+}
+
+static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction *old_instruction) {
+ IrInstruction *new_instruction = ir_build_frame_address(irb, old_instruction->scope, old_instruction->source_node);
+ ir_link_new_instruction(new_instruction, old_instruction);
+ return new_instruction;
+}
+
+static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node,
+ IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,
+ IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)
+{
+ IrInstructionOverflowOp *instruction = ir_build_instruction<IrInstructionOverflowOp>(irb, scope, source_node);
+ instruction->op = op;
+ instruction->type_value = type_value;
+ instruction->op1 = op1;
+ instruction->op2 = op2;
+ instruction->result_ptr = result_ptr;
+ instruction->result_ptr_type = result_ptr_type;
+
+ ir_ref_instruction(type_value);
+ ir_ref_instruction(op1);
+ ir_ref_instruction(op2);
+ ir_ref_instruction(result_ptr);
+
+ return &instruction->base;
+}
+
+static IrInstruction *ir_build_overflow_op_from(IrBuilder *irb, IrInstruction *old_instruction,
+ IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,
+ IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)
+{
+ IrInstruction *new_instruction = ir_build_overflow_op(irb, old_instruction->scope, old_instruction->source_node,
+ op, type_value, op1, op2, result_ptr, result_ptr_type);
+ ir_link_new_instruction(new_instruction, old_instruction);
+ return new_instruction;
+}
+
+static IrInstruction *ir_build_alignof(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) {
+ IrInstructionAlignOf *instruction = ir_build_instruction<IrInstructionAlignOf>(irb, scope, source_node);
+ instruction->type_value = type_value;
+
+ ir_ref_instruction(type_value);
+
+ return &instruction->base;
+}
+
static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope,
bool gen_error_defers, bool gen_maybe_defers)
{
@@ -2142,6 +2219,34 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode
return ir_build_load_ptr(irb, scope, node, ptr_instruction);
}
+static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) {
+ assert(node->type == NodeTypeFnCallExpr);
+
+ AstNode *type_node = node->data.fn_call_expr.params.at(0);
+ AstNode *op1_node = node->data.fn_call_expr.params.at(1);
+ AstNode *op2_node = node->data.fn_call_expr.params.at(2);
+ AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3);
+
+
+ IrInstruction *type_value = ir_gen_node(irb, type_node, scope);
+ if (type_value == irb->codegen->invalid_instruction)
+ return irb->codegen->invalid_instruction;
+
+ IrInstruction *op1 = ir_gen_node(irb, op1_node, scope);
+ if (op1 == irb->codegen->invalid_instruction)
+ return irb->codegen->invalid_instruction;
+
+ IrInstruction *op2 = ir_gen_node(irb, op2_node, scope);
+ if (op2 == irb->codegen->invalid_instruction)
+ return irb->codegen->invalid_instruction;
+
+ IrInstruction *result_ptr = ir_gen_node(irb, result_ptr_node, scope);
+ if (result_ptr == irb->codegen->invalid_instruction)
+ return irb->codegen->invalid_instruction;
+
+ return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr);
+}
+
static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
@@ -2522,14 +2627,27 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
}
case BuiltinFnIdBreakpoint:
return ir_build_breakpoint(irb, scope, node);
+ case BuiltinFnIdReturnAddress:
+ return ir_build_return_address(irb, scope, node);
+ case BuiltinFnIdFrameAddress:
+ return ir_build_frame_address(irb, scope, node);
case BuiltinFnIdAlignof:
+ {
+ AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+ IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+ if (arg0_value == irb->codegen->invalid_instruction)
+ return arg0_value;
+
+ return ir_build_alignof(irb, scope, node, arg0_value);
+ }
case BuiltinFnIdAddWithOverflow:
+ return ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd);
case BuiltinFnIdSubWithOverflow:
+ return ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub);
case BuiltinFnIdMulWithOverflow:
+ return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul);
case BuiltinFnIdShlWithOverflow:
- case BuiltinFnIdReturnAddress:
- case BuiltinFnIdFrameAddress:
- zig_panic("TODO IR gen more builtin functions");
+ return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl);
}
zig_unreachable();
}
@@ -2749,10 +2867,6 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
type_instruction = nullptr;
}
- IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);
- if (init_value == irb->codegen->invalid_instruction)
- return init_value;
-
bool is_shadowable = false;
bool is_const = variable_declaration->is_const;
bool is_extern = variable_declaration->is_extern;
@@ -2768,6 +2882,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
return irb->codegen->invalid_instruction;
}
+ IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);
+ if (init_value == irb->codegen->invalid_instruction)
+ return init_value;
+
return ir_build_var_decl(irb, scope, node, var, type_instruction, init_value);
}
@@ -4057,9 +4175,9 @@ static TypeTableEntry *ir_analyze_void(IrAnalyze *ira, IrInstruction *instructio
static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction,
ConstExprValue *pointee, TypeTableEntry *pointee_type, bool depends_on_compile_var,
- ConstPtrSpecial special)
+ ConstPtrSpecial special, bool ptr_is_const)
{
- TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, pointee_type, true);
+ TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, pointee_type, ptr_is_const);
ConstExprValue *const_val = ir_build_const_from(ira, instruction,
depends_on_compile_var || pointee->depends_on_compile_var);
const_val->data.x_ptr.base_ptr = pointee;
@@ -4086,7 +4204,7 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value) {
IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
TypeTableEntry *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
- FnTableEntry *fn_entry, Buf *c_import_buf)
+ FnTableEntry *fn_entry, Buf *c_import_buf, AstNode *source_node)
{
IrExecutable ir_executable = {0};
ir_executable.is_inline = true;
@@ -4122,7 +4240,7 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node
IrInstruction *result = ir_exec_const_result(&analyzed_executable);
if (!result) {
- add_node_error(codegen, node, buf_sprintf("unable to evaluate constant expression"));
+ add_node_error(codegen, source_node, buf_sprintf("unable to evaluate constant expression"));
return codegen->invalid_instruction;
}
@@ -4499,7 +4617,9 @@ static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_inst
ConstExprValue *val = ir_resolve_const(ira, value);
if (!val)
return ira->codegen->builtin_types.entry_invalid;
- return ir_analyze_const_ptr(ira, source_instruction, val, value->type_entry, false, ConstPtrSpecialNone);
+ bool ptr_is_const = true;
+ return ir_analyze_const_ptr(ira, source_instruction, val, value->type_entry,
+ false, ConstPtrSpecialNone, ptr_is_const);
}
TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, value->type_entry, true);
@@ -5230,11 +5350,16 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
var->type = result_type;
assert(var->type);
- if (casted_init_value->static_value.special == ConstValSpecialStatic) {
+ if (casted_init_value->static_value.special != ConstValSpecialRuntime) {
if (var->mem_slot_index != SIZE_MAX) {
assert(var->mem_slot_index < ira->exec_context.mem_slot_count);
ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
*mem_slot = casted_init_value->static_value;
+
+ if (var->is_inline) {
+ ir_build_const_from(ira, &decl_var_instruction->base, false);
+ return ira->codegen->builtin_types.entry_void;
+ }
}
} else if (var->is_inline) {
ir_add_error(ira, &decl_var_instruction->base,
@@ -5403,7 +5528,8 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
// Analyze the fn body block like any other constant expression.
AstNode *body_node = fn_entry->fn_def_node->data.fn_def.body;
IrInstruction *result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
- ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry, nullptr);
+ ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
+ nullptr, call_instruction->base.source_node);
if (result->type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
@@ -6055,7 +6181,7 @@ static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruc
if (mem_slot && mem_slot->special != ConstValSpecialRuntime) {
ConstPtrSpecial ptr_special = var->is_inline ? ConstPtrSpecialInline : ConstPtrSpecialNone;
- return ir_analyze_const_ptr(ira, instruction, mem_slot, var->type, false, ptr_special);
+ return ir_analyze_const_ptr(ira, instruction, mem_slot, var->type, false, ptr_special, var->src_is_const);
} else {
ir_build_var_ptr_from(&ira->new_irb, instruction, var);
return get_pointer_to_type(ira->codegen, var->type, false);
@@ -6270,7 +6396,9 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source
const_val->special = ConstValSpecialStatic;
const_val->data.x_fn = fn_entry;
- return ir_analyze_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry, depends_on_compile_var, ConstPtrSpecialNone);
+ bool ptr_is_const = true;
+ return ir_analyze_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry,
+ depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const);
}
case TldIdContainer:
{
@@ -6283,7 +6411,9 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source
const_val->special = ConstValSpecialStatic;
const_val->data.x_type = tld_container->type_entry;
- return ir_analyze_const_ptr(ira, source_instruction, const_val, tld_container->type_entry, depends_on_compile_var, ConstPtrSpecialNone);
+ bool ptr_is_const = true;
+ return ir_analyze_const_ptr(ira, source_instruction, const_val, tld_container->type_entry,
+ depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const);
}
case TldIdTypeDef:
{
@@ -6296,7 +6426,9 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source
const_val->special = ConstValSpecialStatic;
const_val->data.x_type = tld_typedef->type_entry;
- return ir_analyze_const_ptr(ira, source_instruction, const_val, tld_typedef->type_entry, depends_on_compile_var, ConstPtrSpecialNone);
+ bool ptr_is_const = true;
+ return ir_analyze_const_ptr(ira, source_instruction, const_val, tld_typedef->type_entry,
+ depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const);
}
}
zig_unreachable();
@@ -6332,7 +6464,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
bignum_init_unsigned(&len_val->data.x_bignum, container_type->data.array.len);
TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
- return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val, usize, false, ConstPtrSpecialNone);
+ bool ptr_is_const = true;
+ return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val,
+ usize, false, ConstPtrSpecialNone, ptr_is_const);
} else {
add_node_error(ira->codegen, source_node,
buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
@@ -6363,8 +6497,10 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
TypeEnumField *field = find_enum_type_field(child_type, field_name);
if (field) {
if (field->type_entry->id == TypeTableEntryIdVoid) {
- return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_enum_tag(field->value),
- child_type, depends_on_compile_var, ConstPtrSpecialNone);
+ bool ptr_is_const = true;
+ return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
+ create_const_enum_tag(field->value), child_type, depends_on_compile_var,
+ ConstPtrSpecialNone, ptr_is_const);
} else {
zig_panic("TODO enum tag type");
}
@@ -6387,8 +6523,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
const_val->special = ConstValSpecialStatic;
const_val->data.x_pure_err = err_table_entry->value;
+ bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val,
- child_type, depends_on_compile_var, ConstPtrSpecialNone);
+ child_type, depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const);
}
ir_add_error(ira, &field_ptr_instruction->base,
@@ -6396,13 +6533,17 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
return ira->codegen->builtin_types.entry_invalid;
} else if (child_type->id == TypeTableEntryIdInt) {
if (buf_eql_str(field_name, "bit_count")) {
+ bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
create_const_unsigned_negative(child_type->data.integral.bit_count, false),
- ira->codegen->builtin_types.entry_num_lit_int, depends_on_compile_var, ConstPtrSpecialNone);
+ ira->codegen->builtin_types.entry_num_lit_int, depends_on_compile_var,
+ ConstPtrSpecialNone, ptr_is_const);
} else if (buf_eql_str(field_name, "is_signed")) {
+ bool ptr_is_const = true;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
create_const_bool(child_type->data.integral.is_signed),
- ira->codegen->builtin_types.entry_bool, depends_on_compile_var, ConstPtrSpecialNone);
+ ira->codegen->builtin_types.entry_bool, depends_on_compile_var,
+ ConstPtrSpecialNone, ptr_is_const);
} else {
ir_add_error(ira, &field_ptr_instruction->base,
buf_sprintf("type '%s' has no member called '%s'",
@@ -7713,7 +7854,8 @@ static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruc
// Execute the C import block like an inline function
TypeTableEntry *void_type = ira->codegen->builtin_types.entry_void;
IrInstruction *result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type,
- ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, &cimport_scope->buf);
+ ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr,
+ &cimport_scope->buf, block_node);
if (result->type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
@@ -8492,6 +8634,125 @@ static TypeTableEntry *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstr
return ira->codegen->builtin_types.entry_void;
}
+static TypeTableEntry *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstructionReturnAddress *instruction) {
+ ir_build_return_address_from(&ira->new_irb, &instruction->base);
+
+ TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8;
+ TypeTableEntry *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true);
+ return u8_ptr_const;
+}
+
+static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstructionFrameAddress *instruction) {
+ ir_build_frame_address_from(&ira->new_irb, &instruction->base);
+
+ TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8;
+ TypeTableEntry *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true);
+ return u8_ptr_const;
+}
+
+static TypeTableEntry *ir_analyze_instruction_alignof(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
+ IrInstruction *type_value = instruction->type_value->other;
+ if (type_value->type_entry->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+ TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
+
+ if (type_entry->id == TypeTableEntryIdInvalid) {
+ return ira->codegen->builtin_types.entry_invalid;
+ } else if (type_entry->id == TypeTableEntryIdUnreachable) {
+ add_node_error(ira->codegen, first_executing_node(instruction->type_value->source_node),
+ buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ } else {
+ uint64_t align_in_bytes = LLVMABISizeOfType(ira->codegen->target_data_ref, type_entry->type_ref);
+ bool depends_on_compile_var = type_value->static_value.depends_on_compile_var;
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
+ bignum_init_unsigned(&out_val->data.x_bignum, align_in_bytes);
+ return ira->codegen->builtin_types.entry_num_lit_int;
+ }
+}
+
+static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstructionOverflowOp *instruction) {
+ IrInstruction *type_value = instruction->type_value->other;
+ if (type_value->type_entry->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+ TypeTableEntry *dest_type = ir_resolve_type(ira, type_value);
+ TypeTableEntry *canon_type = get_underlying_type(dest_type);
+ if (canon_type->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ if (canon_type->id != TypeTableEntryIdInt) {
+ ir_add_error(ira, type_value,
+ buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
+ // TODO if this is a typedecl, add error note showing the declaration of the type decl
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ IrInstruction *op1 = instruction->op1->other;
+ if (op1->type_entry->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type);
+ if (casted_op1->type_entry->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ IrInstruction *op2 = instruction->op2->other;
+ if (op2->type_entry->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, dest_type);
+ if (casted_op2->type_entry->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ IrInstruction *result_ptr = instruction->result_ptr->other;
+ if (result_ptr->type_entry->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ TypeTableEntry *expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false);
+ IrInstruction *casted_result_ptr = ir_implicit_cast(ira, result_ptr, expected_ptr_type);
+ if (casted_result_ptr->type_entry->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ if (casted_op1->static_value.special == ConstValSpecialStatic &&
+ casted_op2->static_value.special == ConstValSpecialStatic &&
+ casted_result_ptr->static_value.special == ConstValSpecialStatic)
+ {
+ bool depends_on_compile_var = type_value->static_value.depends_on_compile_var ||
+ casted_op1->static_value.depends_on_compile_var || casted_op2->static_value.depends_on_compile_var ||
+ casted_result_ptr->static_value.depends_on_compile_var;
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
+ BigNum *op1_bignum = &casted_op1->static_value.data.x_bignum;
+ BigNum *op2_bignum = &casted_op2->static_value.data.x_bignum;
+ ConstExprValue *pointee_val = const_ptr_pointee(&casted_result_ptr->static_value);
+ BigNum *dest_bignum = &pointee_val->data.x_bignum;
+ switch (instruction->op) {
+ case IrOverflowOpAdd:
+ out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
+ break;
+ case IrOverflowOpSub:
+ out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
+ break;
+ case IrOverflowOpMul:
+ out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
+ break;
+ case IrOverflowOpShl:
+ out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
+ break;
+ }
+ if (!bignum_fits_in_bits(dest_bignum, canon_type->data.integral.bit_count,
+ canon_type->data.integral.is_signed))
+ {
+ out_val->data.x_bool = true;
+ bignum_truncate(dest_bignum, canon_type->data.integral.bit_count);
+ }
+ pointee_val->special = ConstValSpecialStatic;
+ return ira->codegen->builtin_types.entry_bool;
+ }
+
+ ir_build_overflow_op_from(&ira->new_irb, &instruction->base, instruction->op, type_value,
+ casted_op1, casted_op2, casted_result_ptr, dest_type);
+ return ira->codegen->builtin_types.entry_bool;
+}
+
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
@@ -8618,6 +8879,14 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_member_count(ira, (IrInstructionMemberCount *)instruction);
case IrInstructionIdBreakpoint:
return ir_analyze_instruction_breakpoint(ira, (IrInstructionBreakpoint *)instruction);
+ case IrInstructionIdReturnAddress:
+ return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);
+ case IrInstructionIdFrameAddress:
+ return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);
+ case IrInstructionIdAlignOf:
+ return ir_analyze_instruction_alignof(ira, (IrInstructionAlignOf *)instruction);
+ case IrInstructionIdOverflowOp:
+ return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction);
case IrInstructionIdCast:
case IrInstructionIdStructFieldPtr:
case IrInstructionIdEnumFieldPtr:
@@ -8722,6 +8991,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdMemset:
case IrInstructionIdMemcpy:
case IrInstructionIdBreakpoint:
+ case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free
return true;
case IrInstructionIdPhi:
case IrInstructionIdUnOp:
@@ -8765,6 +9035,9 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdAlloca:
case IrInstructionIdSlice:
case IrInstructionIdMemberCount:
+ case IrInstructionIdAlignOf:
+ case IrInstructionIdReturnAddress:
+ case IrInstructionIdFrameAddress:
return false;
case IrInstructionIdAsm:
{
@@ -8776,63 +9049,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
}
// TODO port over all this commented out code into new IR way of doing things
-//static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-// TypeTableEntry *expected_type, AstNode *node)
-//{
-//
-// switch (builtin_fn->id) {
-// case BuiltinFnIdInvalid:
-// zig_unreachable();
-// case BuiltinFnIdAddWithOverflow:
-// case BuiltinFnIdSubWithOverflow:
-// case BuiltinFnIdMulWithOverflow:
-// case BuiltinFnIdShlWithOverflow:
-// {
-// AstNode *type_node = node->data.fn_call_expr.params.at(0);
-// TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node);
-// if (int_type->id == TypeTableEntryIdInvalid) {
-// return g->builtin_types.entry_bool;
-// } else if (int_type->id == TypeTableEntryIdInt) {
-// AstNode *op1_node = node->data.fn_call_expr.params.at(1);
-// AstNode *op2_node = node->data.fn_call_expr.params.at(2);
-// AstNode *result_node = node->data.fn_call_expr.params.at(3);
-//
-// analyze_expression(g, import, context, int_type, op1_node);
-// analyze_expression(g, import, context, int_type, op2_node);
-// analyze_expression(g, import, context, get_pointer_to_type(g, int_type, false),
-// result_node);
-// } else {
-// add_node_error(g, type_node,
-// buf_sprintf("expected integer type, found '%s'", buf_ptr(&int_type->name)));
-// }
-//
-// // TODO constant expression evaluation
-//
-// return g->builtin_types.entry_bool;
-// }
-// case BuiltinFnIdAlignof:
-// {
-// AstNode *type_node = node->data.fn_call_expr.params.at(0);
-// TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
-// if (type_entry->id == TypeTableEntryIdInvalid) {
-// return g->builtin_types.entry_invalid;
-// } else if (type_entry->id == TypeTableEntryIdUnreachable) {
-// add_node_error(g, first_executing_node(type_node),
-// buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name)));
-// return g->builtin_types.entry_invalid;
-// } else {
-// uint64_t align_in_bytes = LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref);
-// return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
-// align_in_bytes, false);
-// }
-// }
-// case BuiltinFnIdReturnAddress:
-// case BuiltinFnIdFrameAddress:
-// mark_impure_fn(g, context, node);
-// return builtin_fn->return_type;
-// }
-// zig_unreachable();
-//}
//static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
// TypeTableEntry *expected_type, AstNode *node)
@@ -8948,110 +9164,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
// }
//}
//
-//static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {
-// assert(node->type == NodeTypeFnCallExpr);
-//
-// size_t fn_call_param_count = node->data.fn_call_expr.params.length;
-// assert(fn_call_param_count == 4);
-//
-// TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
-// assert(int_type->id == TypeTableEntryIdInt);
-//
-// LLVMValueRef val1 = gen_expr(g, node->data.fn_call_expr.params.at(1));
-// LLVMValueRef val2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
-// LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3));
-//
-// LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
-// LLVMValueRef orig_val;
-// if (int_type->data.integral.is_signed) {
-// orig_val = LLVMBuildAShr(g->builder, result, val2, "");
-// } else {
-// orig_val = LLVMBuildLShr(g->builder, result, val2, "");
-// }
-// LLVMValueRef overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, val1, orig_val, "");
-//
-// LLVMBuildStore(g->builder, result, ptr_result);
-//
-// return overflow_bit;
-//}
-//
-//static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
-// assert(node->type == NodeTypeFnCallExpr);
-// AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-// assert(fn_ref_expr->type == NodeTypeSymbol);
-// BuiltinFnEntry *builtin_fn = node->data.fn_call_expr.builtin_fn;
-//
-// switch (builtin_fn->id) {
-// case BuiltinFnIdInvalid:
-// case BuiltinFnIdTypeof:
-// zig_unreachable();
-// case BuiltinFnIdAddWithOverflow:
-// case BuiltinFnIdSubWithOverflow:
-// case BuiltinFnIdMulWithOverflow:
-// {
-// size_t fn_call_param_count = node->data.fn_call_expr.params.length;
-// assert(fn_call_param_count == 4);
-//
-// TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
-// AddSubMul add_sub_mul;
-// if (builtin_fn->id == BuiltinFnIdAddWithOverflow) {
-// add_sub_mul = AddSubMulAdd;
-// } else if (builtin_fn->id == BuiltinFnIdSubWithOverflow) {
-// add_sub_mul = AddSubMulSub;
-// } else if (builtin_fn->id == BuiltinFnIdMulWithOverflow) {
-// add_sub_mul = AddSubMulMul;
-// } else {
-// zig_unreachable();
-// }
-// LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul);
-//
-// LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(1));
-// LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
-// LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3));
-//
-// LLVMValueRef params[] = {
-// op1,
-// op2,
-// };
-//
-// LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
-// LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
-// LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
-// LLVMBuildStore(g->builder, result, ptr_result);
-//
-// return overflow_bit;
-// }
-// case BuiltinFnIdShlWithOverflow:
-// return gen_shl_with_overflow(g, node);
-// case BuiltinFnIdAlignof:
-// case BuiltinFnIdMinValue:
-// case BuiltinFnIdMaxValue:
-// // caught by constant expression eval codegen
-// zig_unreachable();
-// case BuiltinFnIdCompileVar:
-// return nullptr;
-// case BuiltinFnIdFrameAddress:
-// case BuiltinFnIdReturnAddress:
-// {
-// LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref);
-// return LLVMBuildCall(g->builder, builtin_fn->fn_val, &zero, 1, "");
-// }
-// case BuiltinFnIdCmpExchange:
-// return gen_cmp_exchange(g, node);
-// case BuiltinFnIdFence:
-// return gen_fence(g, node);
-// case BuiltinFnIdUnreachable:
-// zig_panic("moved to ir render");
-// case BuiltinFnIdSetFnTest:
-// case BuiltinFnIdSetFnStaticEval:
-// case BuiltinFnIdSetFnNoInline:
-// case BuiltinFnIdSetDebugSafety:
-// // do nothing
-// return nullptr;
-// }
-// zig_unreachable();
-//}
-//
//static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntry *enum_type,
// AstNode *arg_node)
//{
src/ir.hpp
@@ -15,7 +15,7 @@ IrInstruction *ir_gen_fn(CodeGen *g, FnTableEntry *fn_entry);
IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
TypeTableEntry *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
- FnTableEntry *fn_entry, Buf *c_import_buf);
+ FnTableEntry *fn_entry, Buf *c_import_buf, AstNode *source_node);
TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable,
TypeTableEntry *expected_type, AstNode *expected_type_source_node);
src/ir_print.cpp
@@ -816,6 +816,45 @@ static void ir_print_breakpoint(IrPrint *irp, IrInstructionBreakpoint *instructi
fprintf(irp->f, "@breakpoint()");
}
+static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *instruction) {
+ fprintf(irp->f, "@frameAddress()");
+}
+
+static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) {
+ fprintf(irp->f, "@returnAddress()");
+}
+
+static void ir_print_alignof(IrPrint *irp, IrInstructionAlignOf *instruction) {
+ fprintf(irp->f, "@alignOf(");
+ ir_print_other_instruction(irp, instruction->type_value);
+ fprintf(irp->f, ")");
+}
+
+static void ir_print_overflow_op(IrPrint *irp, IrInstructionOverflowOp *instruction) {
+ switch (instruction->op) {
+ case IrOverflowOpAdd:
+ fprintf(irp->f, "@addWithOverflow(");
+ break;
+ case IrOverflowOpSub:
+ fprintf(irp->f, "@subWithOverflow(");
+ break;
+ case IrOverflowOpMul:
+ fprintf(irp->f, "@mulWithOverflow(");
+ break;
+ case IrOverflowOpShl:
+ fprintf(irp->f, "@shlWithOverflow(");
+ break;
+ }
+ ir_print_other_instruction(irp, instruction->type_value);
+ fprintf(irp->f, ", ");
+ ir_print_other_instruction(irp, instruction->op1);
+ fprintf(irp->f, ", ");
+ ir_print_other_instruction(irp, instruction->op2);
+ fprintf(irp->f, ", ");
+ ir_print_other_instruction(irp, instruction->result_ptr);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
switch (instruction->id) {
@@ -1016,6 +1055,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdBreakpoint:
ir_print_breakpoint(irp, (IrInstructionBreakpoint *)instruction);
break;
+ case IrInstructionIdReturnAddress:
+ ir_print_return_address(irp, (IrInstructionReturnAddress *)instruction);
+ break;
+ case IrInstructionIdFrameAddress:
+ ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction);
+ break;
+ case IrInstructionIdAlignOf:
+ ir_print_alignof(irp, (IrInstructionAlignOf *)instruction);
+ break;
+ case IrInstructionIdOverflowOp:
+ ir_print_overflow_op(irp, (IrInstructionOverflowOp *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
test/self_hosted2.zig
@@ -329,6 +329,20 @@ fn intTypeBuiltin() {
}
+fn overflowIntrinsics() {
+ var result: u8 = undefined;
+ assert(@addWithOverflow(u8, 250, 100, &result));
+ assert(!@addWithOverflow(u8, 100, 150, &result));
+ assert(result == 250);
+}
+
+fn shlWithOverflow() {
+ var result: u16 = undefined;
+ assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
+ assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
+ assert(result == 0b1011111111111100);
+}
+
fn assert(ok: bool) {
if (!ok)
@unreachable();
@@ -361,6 +375,8 @@ fn runAllTests() {
exactDivision();
truncate();
intTypeBuiltin();
+ overflowIntrinsics();
+ shlWithOverflow();
}
export nakedcc fn _start() -> unreachable {