Skip to content

Commit

Permalink
Fix generation of invalid LLVM IR (#4506)
Browse files Browse the repository at this point in the history
Fix #4475

Before this change, `LLVM` reported an error during module verification when trying to compile the code referenced in issue #4475:

```pony
class Foo
  new create(a: U32) ? =>
    error

actor Main
  new create(env: Env) =>
    try
      let f = Foo(1)?
    end
```
This bug was caused by the failure to check for the presence of terminator instructions when generating `ret` instructions. So now, with this change, there's a check for the absence of a terminator instruction each time a `ret` instruction is generated.
  • Loading branch information
ArthurPV authored Apr 30, 2024
1 parent da58ca8 commit b949388
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 95 deletions.
15 changes: 15 additions & 0 deletions .release-notes/4506.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Fix generation of invalid LLVM IR

Previously, this code failed at LLVM module verification. Now, with this change, it's fixed by stopping the generation of `ret` instructions after terminator instructions:

```pony
class Foo
new create(a: U32) ? =>
error
actor Main
new create(env: Env) =>
try
let f = Foo(1)?
end
```
4 changes: 2 additions & 2 deletions src/libponyc/codegen/gencontrol.c
Original file line number Diff line number Diff line change
Expand Up @@ -600,10 +600,10 @@ LLVMValueRef gen_return(compile_t* c, ast_t* ast)
LLVMValueRef ret = gen_assign_cast(c, r_type, value, type);
ast_free_unattached(type);
codegen_scope_lifetime_end(c);
LLVMBuildRet(c->builder, ret);
genfun_build_ret(c, ret);
} else {
codegen_scope_lifetime_end(c);
LLVMBuildRetVoid(c->builder);
genfun_build_ret_void(c);
}

codegen_debugloc(c, NULL);
Expand Down
2 changes: 1 addition & 1 deletion src/libponyc/codegen/gendesc.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ static LLVMValueRef make_unbox_function(compile_t* c, reach_type_t* t,

LLVMValueRef result = codegen_call(c, LLVMGlobalGetValueType(c_m->func),
c_m->func, args, count, m->cap != TK_AT);
LLVMBuildRet(c->builder, result);
genfun_build_ret(c, result);
codegen_finishfun(c);

ponyint_pool_free_size(buf_size, params);
Expand Down
2 changes: 1 addition & 1 deletion src/libponyc/codegen/genexe.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ LLVMValueRef gen_main(compile_t* c, reach_type_t* t_main, reach_type_t* t_env)
rc = LLVMBuildSelect(c->builder, start_success, rc, minus_one, "");

// Return the runtime exit code.
LLVMBuildRet(c->builder, rc);
genfun_build_ret(c, rc);

codegen_finishfun(c);

Expand Down
60 changes: 46 additions & 14 deletions src/libponyc/codegen/genfun.c
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ static void add_dispatch_case(compile_t* c, reach_type_t* t,

// Call the handler.
codegen_call(c, LLVMGlobalGetValueType(handler), handler, args, count, true);
LLVMBuildRetVoid(c->builder);
genfun_build_ret_void(c);
codegen_finishfun(c);
ponyint_pool_free_size(args_buf_size, args);
ponyint_pool_free_size(params_buf_size, param_types);
Expand Down Expand Up @@ -422,6 +422,36 @@ static void call_embed_finalisers(compile_t* c, reach_type_t* t,
}
}

static bool
genfun_has_terminator(compile_t* c)
{
LLVMBasicBlockRef current_block = LLVMGetInsertBlock(c->builder);

pony_assert(current_block);

return LLVMGetBasicBlockTerminator(current_block);
}

LLVMValueRef
genfun_build_ret(compile_t* c, LLVMValueRef v)
{
if (!genfun_has_terminator(c)) {
return LLVMBuildRet(c->builder, v);
}

return NULL;
}

LLVMValueRef
genfun_build_ret_void(compile_t* c)
{
if (!genfun_has_terminator(c)) {
return LLVMBuildRetVoid(c->builder);
}

return NULL;
}

static bool genfun_fun(compile_t* c, reach_type_t* t, reach_method_t* m)
{
compile_type_t* c_t = (compile_type_t*)t->c_type;
Expand Down Expand Up @@ -454,7 +484,8 @@ static bool genfun_fun(compile_t* c, reach_type_t* t, reach_method_t* m)
ast_free_unattached(r_result);
codegen_scope_lifetime_end(c);
codegen_debugloc(c, ast_childlast(body));
LLVMBuildRetVoid(c->builder);

genfun_build_ret_void(c);
} else {
LLVMTypeRef f_type = LLVMGlobalGetValueType(c_m->func);
LLVMTypeRef r_type = LLVMGetReturnType(f_type);
Expand All @@ -470,7 +501,8 @@ static bool genfun_fun(compile_t* c, reach_type_t* t, reach_method_t* m)

codegen_scope_lifetime_end(c);
codegen_debugloc(c, ast_childlast(body));
LLVMBuildRet(c->builder, ret);

genfun_build_ret(c, ret);
}

codegen_debugloc(c, NULL);
Expand Down Expand Up @@ -502,7 +534,7 @@ static bool genfun_be(compile_t* c, reach_type_t* t, reach_method_t* m)

codegen_scope_lifetime_end(c);
if(value != GEN_NOVALUE)
LLVMBuildRetVoid(c->builder);
genfun_build_ret_void(c);

codegen_finishfun(c);

Expand All @@ -516,7 +548,7 @@ static bool genfun_be(compile_t* c, reach_type_t* t, reach_method_t* m)
gen_send_message(c, m, param_vals, params);

// Return None.
LLVMBuildRet(c->builder, c->none_instance);
genfun_build_ret(c, c->none_instance);
codegen_finishfun(c);

ponyint_pool_free_size(buf_size, param_vals);
Expand Down Expand Up @@ -552,9 +584,9 @@ static bool genfun_new(compile_t* c, reach_type_t* t, reach_method_t* m)
codegen_scope_lifetime_end(c);
codegen_debugloc(c, ast_childlast(body));
if(t->underlying == TK_CLASS)
LLVMBuildRetVoid(c->builder);
genfun_build_ret_void(c);
else
LLVMBuildRet(c->builder, value);
genfun_build_ret(c, value);
codegen_debugloc(c, NULL);

codegen_finishfun(c);
Expand Down Expand Up @@ -582,7 +614,7 @@ static bool genfun_newbe(compile_t* c, reach_type_t* t, reach_method_t* m)
return false;

codegen_scope_lifetime_end(c);
LLVMBuildRetVoid(c->builder);
genfun_build_ret_void(c);
codegen_finishfun(c);

// Generate the sender.
Expand All @@ -595,7 +627,7 @@ static bool genfun_newbe(compile_t* c, reach_type_t* t, reach_method_t* m)
gen_send_message(c, m, param_vals, params);

// Return 'this'.
LLVMBuildRet(c->builder, param_vals[0]);
genfun_build_ret(c, param_vals[0]);
codegen_finishfun(c);

ponyint_pool_free_size(buf_size, param_vals);
Expand Down Expand Up @@ -642,7 +674,7 @@ static bool genfun_implicit_final(compile_t* c, reach_type_t* t,

codegen_startfun(c, c_m->func, NULL, NULL, NULL, false);
call_embed_finalisers(c, t, NULL, gen_this(c, NULL));
LLVMBuildRetVoid(c->builder);
genfun_build_ret_void(c);
codegen_finishfun(c);

return true;
Expand Down Expand Up @@ -708,7 +740,7 @@ static bool genfun_allocator(compile_t* c, reach_type_t* t)
return false;
}

LLVMBuildRet(c->builder, result);
genfun_build_ret(c, result);
codegen_finishfun(c);
return true;
}
Expand Down Expand Up @@ -747,7 +779,7 @@ static bool genfun_forward(compile_t* c, reach_type_t* t,
codegen_debugloc(c, NULL);
ret = gen_assign_cast(c, ((compile_type_t*)m->result->c_type)->use_type, ret,
m2->result->ast_cap);
LLVMBuildRet(c->builder, ret);
genfun_build_ret(c, ret);
codegen_finishfun(c);
ponyint_pool_free_size(buf_size, args);
return true;
Expand Down Expand Up @@ -1033,7 +1065,7 @@ void genfun_primitive_calls(compile_t* c)

codegen_startfun(c, c->primitives_init, NULL, NULL, NULL, false);
primitive_call(c, c->str__init);
LLVMBuildRetVoid(c->builder);
genfun_build_ret_void(c);
codegen_finishfun(c);
}

Expand All @@ -1046,7 +1078,7 @@ void genfun_primitive_calls(compile_t* c)

codegen_startfun(c, c->primitives_final, NULL, NULL, NULL, false);
primitive_call(c, c->str__final);
LLVMBuildRetVoid(c->builder);
genfun_build_ret_void(c);
codegen_finishfun(c);
}
}
8 changes: 8 additions & 0 deletions src/libponyc/codegen/genfun.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ bool genfun_method_bodies(compile_t* c, reach_type_t* t);

void genfun_primitive_calls(compile_t* c);

bool genfun_last_inst_is_terminator(compile_t* c);

LLVMValueRef
genfun_build_ret(compile_t* c, LLVMValueRef v);

LLVMValueRef
genfun_build_ret_void(compile_t* c);

PONY_EXTERN_C_END

#endif
2 changes: 1 addition & 1 deletion src/libponyc/codegen/genident.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ void gen_is_tuple_fun(compile_t* c, reach_type_t* t)
// box_is_box(). Don't recheck it in tuple_is_box().
LLVMValueRef same_identity = tuple_is_box(c, t->ast_cap, NULL, l_value,
r_value, r_desc, true, false);
LLVMBuildRet(c->builder, same_identity);
genfun_build_ret(c, same_identity);

codegen_finishfun(c);
}
Expand Down
Loading

0 comments on commit b949388

Please sign in to comment.