IR: support setDebugSafety builtin function

master
Andrew Kelley 2016-11-18 20:57:27 -05:00
parent ed31ae8867
commit bf7cde62c5
6 changed files with 168 additions and 77 deletions

View File

@ -1449,6 +1449,7 @@ enum IrInstructionId {
IrInstructionIdToPtrType, IrInstructionIdToPtrType,
IrInstructionIdPtrTypeChild, IrInstructionIdPtrTypeChild,
IrInstructionIdSetFnTest, IrInstructionIdSetFnTest,
IrInstructionIdSetDebugSafety,
IrInstructionIdArrayType, IrInstructionIdArrayType,
IrInstructionIdSliceType, IrInstructionIdSliceType,
IrInstructionIdAsm, IrInstructionIdAsm,
@ -1705,6 +1706,13 @@ struct IrInstructionSetFnTest {
IrInstruction *is_test; IrInstruction *is_test;
}; };
struct IrInstructionSetDebugSafety {
IrInstruction base;
IrInstruction *scope_value;
IrInstruction *debug_safety_on;
};
struct IrInstructionArrayType { struct IrInstructionArrayType {
IrInstruction base; IrInstruction base;

View File

@ -669,6 +669,17 @@ static void render_node(AstRender *ar, AstNode *node) {
render_node(ar, node->data.while_expr.body); render_node(ar, node->data.while_expr.body);
break; break;
} }
case NodeTypeThisLiteral:
{
fprintf(ar->f, "this");
break;
}
case NodeTypeBoolLiteral:
{
const char *bool_str = node->data.bool_literal.value ? "true" : "false";
fprintf(ar->f, "%s", bool_str);
break;
}
case NodeTypeFnDecl: case NodeTypeFnDecl:
case NodeTypeParamDecl: case NodeTypeParamDecl:
case NodeTypeErrorValueDecl: case NodeTypeErrorValueDecl:
@ -677,10 +688,8 @@ static void render_node(AstRender *ar, AstNode *node) {
case NodeTypeStructField: case NodeTypeStructField:
case NodeTypeStructValueField: case NodeTypeStructValueField:
case NodeTypeUse: case NodeTypeUse:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral: case NodeTypeNullLiteral:
case NodeTypeZeroesLiteral: case NodeTypeZeroesLiteral:
case NodeTypeThisLiteral:
case NodeTypeIfBoolExpr: case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr: case NodeTypeIfVarExpr:
case NodeTypeForExpr: case NodeTypeForExpr:

View File

@ -1681,6 +1681,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdPtrTypeChild: case IrInstructionIdPtrTypeChild:
case IrInstructionIdFieldPtr: case IrInstructionIdFieldPtr:
case IrInstructionIdSetFnTest: case IrInstructionIdSetFnTest:
case IrInstructionIdSetDebugSafety:
case IrInstructionIdArrayType: case IrInstructionIdArrayType:
case IrInstructionIdSliceType: case IrInstructionIdSliceType:
zig_unreachable(); zig_unreachable();

View File

@ -201,6 +201,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnTest *) {
return IrInstructionIdSetFnTest; return IrInstructionIdSetFnTest;
} }
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety *) {
return IrInstructionIdSetDebugSafety;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) { static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
return IrInstructionIdArrayType; return IrInstructionIdArrayType;
} }
@ -786,6 +790,19 @@ static IrInstruction *ir_build_set_fn_test(IrBuilder *irb, AstNode *source_node,
return &instruction->base; return &instruction->base;
} }
static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, AstNode *source_node,
IrInstruction *scope_value, IrInstruction *debug_safety_on)
{
IrInstructionSetDebugSafety *instruction = ir_build_instruction<IrInstructionSetDebugSafety>(irb, source_node);
instruction->scope_value = scope_value;
instruction->debug_safety_on = debug_safety_on;
ir_ref_instruction(scope_value);
ir_ref_instruction(debug_safety_on);
return &instruction->base;
}
static IrInstruction *ir_build_array_type(IrBuilder *irb, AstNode *source_node, IrInstruction *size, static IrInstruction *ir_build_array_type(IrBuilder *irb, AstNode *source_node, IrInstruction *size,
IrInstruction *child_type) IrInstruction *child_type)
{ {
@ -1291,6 +1308,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
return ir_build_set_fn_test(irb, node, arg0_value, arg1_value); return ir_build_set_fn_test(irb, node, arg0_value, arg1_value);
} }
case BuiltinFnIdSetDebugSafety:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, node->block_context);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, node->block_context);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_set_debug_safety(irb, node, arg0_value, arg1_value);
}
case BuiltinFnIdMemcpy: case BuiltinFnIdMemcpy:
case BuiltinFnIdMemset: case BuiltinFnIdMemset:
case BuiltinFnIdSizeof: case BuiltinFnIdSizeof:
@ -1325,7 +1356,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
case BuiltinFnIdSetFnVisible: case BuiltinFnIdSetFnVisible:
case BuiltinFnIdSetFnStaticEval: case BuiltinFnIdSetFnStaticEval:
case BuiltinFnIdSetFnNoInline: case BuiltinFnIdSetFnNoInline:
case BuiltinFnIdSetDebugSafety:
zig_panic("TODO IR gen more builtin functions"); zig_panic("TODO IR gen more builtin functions");
} }
zig_unreachable(); zig_unreachable();
@ -2260,6 +2290,15 @@ static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value
return const_val->data.x_type; return const_val->data.x_type;
} }
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value) {
if (value->static_value.special != ConstValSpecialStatic) {
add_node_error(ira->codegen, value->source_node,
buf_sprintf("unable to evaluate constant expression"));
return nullptr;
}
return &value->static_value;
}
static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *bool_value, bool *out) { static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *bool_value, bool *out) {
if (bool_value == ira->codegen->invalid_instruction) if (bool_value == ira->codegen->invalid_instruction)
return false; return false;
@ -2273,12 +2312,9 @@ static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *bool_value, bool *out
return false; return false;
} }
ConstExprValue *const_val = &bool_value->static_value; ConstExprValue *const_val = ir_resolve_const(ira, bool_value);
if (const_val->special == ConstValSpecialRuntime) { if (!const_val)
add_node_error(ira->codegen, bool_value->source_node,
buf_sprintf("unable to evaluate constant expression"));
return false; return false;
}
*out = const_val->data.x_bool; *out = const_val->data.x_bool;
return true; return true;
@ -4027,12 +4063,67 @@ static TypeTableEntry *ir_analyze_instruction_set_fn_test(IrAnalyze *ira,
} }
fn_entry->fn_test_set_node = source_node; fn_entry->fn_test_set_node = source_node;
if (fn_entry->is_test)
ira->codegen->test_fn_count += 1; ira->codegen->test_fn_count += 1;
ir_build_const_from(ira, &set_fn_test_instruction->base, false); ir_build_const_from(ira, &set_fn_test_instruction->base, false);
return ira->codegen->builtin_types.entry_void; return ira->codegen->builtin_types.entry_void;
} }
static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
IrInstructionSetDebugSafety *set_debug_safety_instruction)
{
IrInstruction *target_instruction = set_debug_safety_instruction->scope_value->other;
TypeTableEntry *target_type = target_instruction->type_entry;
if (target_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *target_val = ir_resolve_const(ira, target_instruction);
if (!target_val)
return ira->codegen->builtin_types.entry_invalid;
BlockContext *target_context;
if (target_type->id == TypeTableEntryIdBlock) {
target_context = target_val->data.x_block;
} else if (target_type->id == TypeTableEntryIdFn) {
target_context = target_val->data.x_fn->fn_def_node->data.fn_def.block_context;
} else if (target_type->id == TypeTableEntryIdMetaType) {
TypeTableEntry *type_arg = target_val->data.x_type;
if (type_arg->id == TypeTableEntryIdStruct) {
target_context = type_arg->data.structure.block_context;
} else if (type_arg->id == TypeTableEntryIdEnum) {
target_context = type_arg->data.enumeration.block_context;
} else if (type_arg->id == TypeTableEntryIdUnion) {
target_context = type_arg->data.unionation.block_context;
} else {
add_node_error(ira->codegen, target_instruction->source_node,
buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&type_arg->name)));
return ira->codegen->builtin_types.entry_invalid;
}
} else {
add_node_error(ira->codegen, target_instruction->source_node,
buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&target_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
IrInstruction *debug_safety_on_value = set_debug_safety_instruction->debug_safety_on->other;
bool want_debug_safety;
if (!ir_resolve_bool(ira, debug_safety_on_value, &want_debug_safety))
return ira->codegen->builtin_types.entry_invalid;
AstNode *source_node = set_debug_safety_instruction->base.source_node;
if (target_context->safety_set_node) {
ErrorMsg *msg = add_node_error(ira->codegen, source_node,
buf_sprintf("function test attribute set twice"));
add_error_note(ira->codegen, msg, target_context->safety_set_node, buf_sprintf("first set here"));
return ira->codegen->builtin_types.entry_invalid;
}
target_context->safety_set_node = source_node;
target_context->safety_off = !want_debug_safety;
ir_build_const_from(ira, &set_debug_safety_instruction->base, false);
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
IrInstructionSliceType *slice_type_instruction) IrInstructionSliceType *slice_type_instruction)
{ {
@ -4164,6 +4255,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction); return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction);
case IrInstructionIdSetFnTest: case IrInstructionIdSetFnTest:
return ir_analyze_instruction_set_fn_test(ira, (IrInstructionSetFnTest *)instruction); return ir_analyze_instruction_set_fn_test(ira, (IrInstructionSetFnTest *)instruction);
case IrInstructionIdSetDebugSafety:
return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction);
case IrInstructionIdSliceType: case IrInstructionIdSliceType:
return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction); return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
case IrInstructionIdAsm: case IrInstructionIdAsm:
@ -4257,6 +4350,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdReturn: case IrInstructionIdReturn:
case IrInstructionIdUnreachable: case IrInstructionIdUnreachable:
case IrInstructionIdSetFnTest: case IrInstructionIdSetFnTest:
case IrInstructionIdSetDebugSafety:
return true; return true;
case IrInstructionIdPhi: case IrInstructionIdPhi:
case IrInstructionIdUnOp: case IrInstructionIdUnOp:
@ -4854,64 +4948,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return g->builtin_types.entry_void; // return g->builtin_types.entry_void;
//} //}
// //
//static TypeTableEntry *analyze_set_debug_safety(CodeGen *g, ImportTableEntry *import,
// BlockContext *parent_context, AstNode *node)
//{
// AstNode **target_node = &node->data.fn_call_expr.params.at(0);
// AstNode **value_node = &node->data.fn_call_expr.params.at(1);
//
// TypeTableEntry *target_type = analyze_expression(g, import, parent_context, nullptr, *target_node);
// BlockContext *target_context;
// ConstExprValue *const_val = &get_resolved_expr(*target_node)->const_val;
// if (target_type->id == TypeTableEntryIdInvalid) {
// return g->builtin_types.entry_invalid;
// }
// if (!const_val->ok) {
// add_node_error(g, *target_node, buf_sprintf("unable to evaluate constant expression"));
// return g->builtin_types.entry_invalid;
// }
// if (target_type->id == TypeTableEntryIdBlock) {
// target_context = const_val->data.x_block;
// } else if (target_type->id == TypeTableEntryIdFn) {
// target_context = const_val->data.x_fn->fn_def_node->data.fn_def.block_context;
// } else if (target_type->id == TypeTableEntryIdMetaType) {
// TypeTableEntry *type_arg = const_val->data.x_type;
// if (type_arg->id == TypeTableEntryIdStruct) {
// target_context = type_arg->data.structure.block_context;
// } else if (type_arg->id == TypeTableEntryIdEnum) {
// target_context = type_arg->data.enumeration.block_context;
// } else if (type_arg->id == TypeTableEntryIdUnion) {
// target_context = type_arg->data.unionation.block_context;
// } else {
// add_node_error(g, *target_node,
// buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&type_arg->name)));
// return g->builtin_types.entry_invalid;
// }
// } else {
// add_node_error(g, *target_node,
// buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&target_type->name)));
// return g->builtin_types.entry_invalid;
// }
//
// bool want_debug_safety;
// bool ok = resolve_const_expr_bool(g, import, parent_context, value_node, &want_debug_safety);
// if (!ok) {
// return g->builtin_types.entry_invalid;
// }
//
// if (target_context->safety_set_node) {
// ErrorMsg *msg = add_node_error(g, node, buf_sprintf("debug safety for scope set twice"));
// add_error_note(g, msg, target_context->safety_set_node, buf_sprintf("first set here"));
// return g->builtin_types.entry_invalid;
// }
// target_context->safety_set_node = node;
//
// target_context->safety_off = !want_debug_safety;
//
// return g->builtin_types.entry_void;
//}
//static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, //static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
// TypeTableEntry *expected_type, AstNode *node) // TypeTableEntry *expected_type, AstNode *node)
//{ //{
@ -5215,8 +5251,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return analyze_set_fn_static_eval(g, import, context, node); // return analyze_set_fn_static_eval(g, import, context, node);
// case BuiltinFnIdSetFnVisible: // case BuiltinFnIdSetFnVisible:
// return analyze_set_fn_visible(g, import, context, node); // return analyze_set_fn_visible(g, import, context, node);
// case BuiltinFnIdSetDebugSafety:
// return analyze_set_debug_safety(g, import, context, node);
// } // }
// zig_unreachable(); // zig_unreachable();
//} //}

View File

@ -409,6 +409,14 @@ static void ir_print_set_fn_test(IrPrint *irp, IrInstructionSetFnTest *instructi
fprintf(irp->f, ")"); fprintf(irp->f, ")");
} }
static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) {
fprintf(irp->f, "@setDebugSafety(");
ir_print_other_instruction(irp, instruction->scope_value);
fprintf(irp->f, ", ");
ir_print_other_instruction(irp, instruction->debug_safety_on);
fprintf(irp->f, ")");
}
static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instruction) { static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instruction) {
fprintf(irp->f, "["); fprintf(irp->f, "[");
ir_print_other_instruction(irp, instruction->size); ir_print_other_instruction(irp, instruction->size);
@ -541,6 +549,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdSetFnTest: case IrInstructionIdSetFnTest:
ir_print_set_fn_test(irp, (IrInstructionSetFnTest *)instruction); ir_print_set_fn_test(irp, (IrInstructionSetFnTest *)instruction);
break; break;
case IrInstructionIdSetDebugSafety:
ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);
break;
case IrInstructionIdArrayType: case IrInstructionIdArrayType:
ir_print_array_type(irp, (IrInstructionArrayType *)instruction); ir_print_array_type(irp, (IrInstructionArrayType *)instruction);
break; break;

View File

@ -1,13 +1,41 @@
fn add(a: i32, b: i32) -> i32 { pub const SYS_write = 1;
a + b pub const SYS_exit = 60;
pub const stdout_fileno = 1;
const text = "hello\n";
export nakedcc fn _start() -> unreachable {
myMain();
} }
fn assert(ok: bool) { fn myMain() -> unreachable {
if (!ok) @unreachable(); write(stdout_fileno, &text[0], text.len);
exit(0);
} }
fn testAdd() { pub inline fn syscall1(number: usize, arg1: usize) -> usize {
@setFnTest(this, true); asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
assert(add(2, 3) == 5); : [number] "{rax}" (number),
[arg1] "{rdi}" (arg1)
: "rcx", "r11")
} }
pub inline fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1),
[arg2] "{rsi}" (arg2),
[arg3] "{rdx}" (arg3)
: "rcx", "r11")
}
pub fn write(fd: i32, buf: &const u8, count: usize) -> usize {
syscall3(SYS_write, usize(fd), usize(buf), count)
}
pub fn exit(status: i32) -> unreachable {
syscall1(SYS_exit, usize(status));
@unreachable()
}