Skip to content

Commit 257fded

Browse files
committed
avoid setting tail for @panic
Currently, slices are passed via reference, even though it would be better to pass the ptr and len as separate arguments (#561). This means that any function call with a slice parameter cannot be a tail call, because according to LLVM spec: > Both [tail,musttail] markers imply that the callee does not access > allocas from the caller There was one other place we were setting `tail` and I made that conditional on whether or not the argument referenced allocas in the caller. This was causing undefined behavior in the compiler when it hit asserts, causing it to print garbage memory to the terminal. See #3262 for example.
1 parent f663bcd commit 257fded

File tree

1 file changed

+47
-17
lines changed

1 file changed

+47
-17
lines changed

src/codegen.cpp

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,9 @@ static ZigType *ptr_to_stack_trace_type(CodeGen *g) {
964964
return get_pointer_to_type(g, get_stack_trace_type(g), false);
965965
}
966966

967-
static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg) {
967+
static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg,
968+
bool stack_trace_is_llvm_alloca)
969+
{
968970
assert(g->panic_fn != nullptr);
969971
LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);
970972
LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);
@@ -975,14 +977,20 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace
975977
msg_arg,
976978
stack_trace_arg,
977979
};
978-
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, "");
979-
LLVMSetTailCall(call_instruction, true);
980+
ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, "");
981+
if (!stack_trace_is_llvm_alloca) {
982+
// The stack trace argument is not in the stack of the caller, so
983+
// we'd like to set tail call here, but because slices (the type of msg_arg) are
984+
// still passed as pointers (see https://github.com/ziglang/zig/issues/561) we still
985+
// cannot make this a tail call.
986+
//LLVMSetTailCall(call_instruction, true);
987+
}
980988
LLVMBuildUnreachable(g->builder);
981989
}
982990

983991
// TODO update most callsites to call gen_assertion instead of this
984992
static void gen_safety_crash(CodeGen *g, PanicMsgId msg_id) {
985-
gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr);
993+
gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr, false);
986994
}
987995

988996
static void gen_assertion_scope(CodeGen *g, PanicMsgId msg_id, Scope *source_scope) {
@@ -1320,7 +1328,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
13201328
gen_store_untyped(g, slice_len, msg_slice_len_field_ptr, 0, false);
13211329

13221330
// Call panic()
1323-
gen_panic(g, msg_slice, err_ret_trace_arg);
1331+
gen_panic(g, msg_slice, err_ret_trace_arg, false);
13241332

13251333
LLVMPositionBuilderAtEnd(g->builder, prev_block);
13261334
if (!g->strip_debug_symbols) {
@@ -1331,21 +1339,25 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
13311339
return fn_val;
13321340
}
13331341

1334-
static LLVMValueRef get_cur_err_ret_trace_val(CodeGen *g, Scope *scope) {
1342+
static LLVMValueRef get_cur_err_ret_trace_val(CodeGen *g, Scope *scope, bool *is_llvm_alloca) {
13351343
if (!g->have_err_ret_tracing) {
1344+
*is_llvm_alloca = false;
13361345
return nullptr;
13371346
}
13381347
if (g->cur_err_ret_trace_val_stack != nullptr) {
1348+
*is_llvm_alloca = !fn_is_async(g->cur_fn);
13391349
return g->cur_err_ret_trace_val_stack;
13401350
}
1351+
*is_llvm_alloca = false;
13411352
return g->cur_err_ret_trace_val_arg;
13421353
}
13431354

13441355
static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val, Scope *scope) {
13451356
LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
13461357
LLVMValueRef call_instruction;
1358+
bool is_llvm_alloca = false;
13471359
if (g->have_err_ret_tracing) {
1348-
LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, scope);
1360+
LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, scope, &is_llvm_alloca);
13491361
if (err_ret_trace_val == nullptr) {
13501362
err_ret_trace_val = LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type(g)));
13511363
}
@@ -1362,7 +1374,9 @@ static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val, Scope *sc
13621374
call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 1,
13631375
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
13641376
}
1365-
LLVMSetTailCall(call_instruction, true);
1377+
if (!is_llvm_alloca) {
1378+
LLVMSetTailCall(call_instruction, true);
1379+
}
13661380
LLVMBuildUnreachable(g->builder);
13671381
}
13681382

@@ -2202,7 +2216,9 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut
22022216
assert(g->have_err_ret_tracing);
22032217

22042218
LLVMValueRef return_err_fn = get_return_err_fn(g);
2205-
LLVMValueRef my_err_trace_val = get_cur_err_ret_trace_val(g, save_err_ret_addr_instruction->base.scope);
2219+
bool is_llvm_alloca;
2220+
LLVMValueRef my_err_trace_val = get_cur_err_ret_trace_val(g, save_err_ret_addr_instruction->base.scope,
2221+
&is_llvm_alloca);
22062222
ZigLLVMBuildCall(g->builder, return_err_fn, &my_err_trace_val, 1,
22072223
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
22082224

@@ -2256,6 +2272,10 @@ static LLVMBasicBlockRef gen_suspend_begin(CodeGen *g, const char *name_hint) {
22562272
return resume_bb;
22572273
}
22582274

2275+
// Be careful setting tail call. According to LLVM lang ref,
2276+
// tail and musttail imply that the callee does not access allocas from the caller.
2277+
// This works for async functions since the locals are spilled.
2278+
// http://llvm.org/docs/LangRef.html#id320
22592279
static void set_tail_call_if_appropriate(CodeGen *g, LLVMValueRef call_inst) {
22602280
LLVMSetTailCall(call_inst, true);
22612281
}
@@ -2361,7 +2381,8 @@ static void gen_async_return(CodeGen *g, IrInstructionReturn *instruction) {
23612381
LLVMValueRef awaiter_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr,
23622382
frame_index_trace_arg(g, ret_type) + 1, "");
23632383
LLVMValueRef dest_trace_ptr = LLVMBuildLoad(g->builder, awaiter_trace_ptr_ptr, "");
2364-
LLVMValueRef my_err_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope);
2384+
bool is_llvm_alloca;
2385+
LLVMValueRef my_err_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope, &is_llvm_alloca);
23652386
LLVMValueRef args[] = { dest_trace_ptr, my_err_trace_val };
23662387
ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2,
23672388
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
@@ -3967,7 +3988,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
39673988
if (prefix_arg_err_ret_stack) {
39683989
LLVMValueRef err_ret_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc,
39693990
frame_index_trace_arg(g, src_return_type) + 1, "");
3970-
LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope);
3991+
bool is_llvm_alloca;
3992+
LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope,
3993+
&is_llvm_alloca);
39713994
LLVMBuildStore(g->builder, my_err_ret_trace_val, err_ret_trace_ptr_ptr);
39723995
}
39733996
}
@@ -4024,15 +4047,17 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
40244047

40254048
gen_init_stack_trace(g, trace_field_ptr, addrs_field_ptr);
40264049

4027-
gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope));
4050+
bool is_llvm_alloca;
4051+
gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope, &is_llvm_alloca));
40284052
}
40294053
}
40304054
} else {
40314055
if (first_arg_ret) {
40324056
gen_param_values.append(result_loc);
40334057
}
40344058
if (prefix_arg_err_ret_stack) {
4035-
gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope));
4059+
bool is_llvm_alloca;
4060+
gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope, &is_llvm_alloca));
40364061
}
40374062
}
40384063
FnWalk fn_walk = {};
@@ -4912,7 +4937,8 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
49124937
static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable,
49134938
IrInstructionErrorReturnTrace *instruction)
49144939
{
4915-
LLVMValueRef cur_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope);
4940+
bool is_llvm_alloca;
4941+
LLVMValueRef cur_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope, &is_llvm_alloca);
49164942
if (cur_err_ret_trace_val == nullptr) {
49174943
return LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type(g)));
49184944
}
@@ -5496,7 +5522,9 @@ static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, Ir
54965522
}
54975523

54985524
static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) {
5499-
gen_panic(g, ir_llvm_value(g, instruction->msg), get_cur_err_ret_trace_val(g, instruction->base.scope));
5525+
bool is_llvm_alloca;
5526+
LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope, &is_llvm_alloca);
5527+
gen_panic(g, ir_llvm_value(g, instruction->msg), err_ret_trace_val, is_llvm_alloca);
55005528
return nullptr;
55015529
}
55025530

@@ -5753,7 +5781,8 @@ static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstruction *source_ins
57535781
LLVMValueRef their_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr,
57545782
frame_index_trace_arg(g, result_type), "");
57555783
LLVMValueRef src_trace_ptr = LLVMBuildLoad(g->builder, their_trace_ptr_ptr, "");
5756-
LLVMValueRef dest_trace_ptr = get_cur_err_ret_trace_val(g, source_instr->scope);
5784+
bool is_llvm_alloca;
5785+
LLVMValueRef dest_trace_ptr = get_cur_err_ret_trace_val(g, source_instr->scope, &is_llvm_alloca);
57575786
LLVMValueRef args[] = { dest_trace_ptr, src_trace_ptr };
57585787
ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2,
57595788
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
@@ -5802,7 +5831,8 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
58025831

58035832
// supply the error return trace pointer
58045833
if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) {
5805-
LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope);
5834+
bool is_llvm_alloca;
5835+
LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope, &is_llvm_alloca);
58065836
assert(my_err_ret_trace_val != nullptr);
58075837
LLVMValueRef err_ret_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr,
58085838
frame_index_trace_arg(g, result_type) + 1, "");

0 commit comments

Comments
 (0)