Skip to content

Commit

Permalink
Partially mitigate LLVM's infinite loops bug (ponylang#2592)
Browse files Browse the repository at this point in the history
The LLVM optimiser in its current state treats infinite loops as
provoking undefined behaviour, which can lead to very undesirable
effects in Pony programs (for example, an incorrect behaviour being
called on a message receive.)

This change should completely fix the example described above, and
fix some other less catastrophic manifestations of this bug.

There are ongoing efforts to fix these bugs on LLVM's side. Once they
are completely fixed, this workaround can be removed.
  • Loading branch information
Benoit Vey authored and dipinhora committed Jun 5, 2018
1 parent 88a8b14 commit 04fd226
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 10 deletions.
11 changes: 1 addition & 10 deletions src/libponyc/codegen/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,7 @@ static LLVMTargetMachineRef make_machine(pass_opt_t* opt, bool jit)
return NULL;
}

LLVMCodeGenOptLevel opt_level =
opt->release ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone;

LLVMRelocMode reloc =
(opt->pic || opt->library) ? LLVMRelocPIC : LLVMRelocDefault;

LLVMCodeModel model = jit ? LLVMCodeModelJITDefault : LLVMCodeModelDefault;

LLVMTargetMachineRef machine = LLVMCreateTargetMachine(target, opt->triple,
opt->cpu, opt->features, opt_level, reloc, model);
LLVMTargetMachineRef machine = codegen_machine(target, opt, jit);

if(machine == NULL)
{
Expand Down
3 changes: 3 additions & 0 deletions src/libponyc/codegen/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ bool codegen_pass_init(pass_opt_t* opt);

void codegen_pass_cleanup(pass_opt_t* opt);

LLVMTargetMachineRef codegen_machine(LLVMTargetRef target, pass_opt_t* opt,
bool jit);

bool codegen(ast_t* program, pass_opt_t* opt);

bool codegen_gen_test(compile_t* c, ast_t* program, pass_opt_t* opt,
Expand Down
22 changes: 22 additions & 0 deletions src/libponyc/codegen/gentype.c
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,29 @@ static void make_dispatch(compile_t* c, reach_type_t* t)

// Mark the default case as unreachable.
LLVMPositionBuilderAtEnd(c->builder, unreachable);

// Workaround for LLVM's "infinite loops are undefined behaviour".
// If a Pony behaviour contains an infinite loop, the LLVM optimiser in its
// current state can assume that the associated message is never received.
// From there, if the dispatch switch is optimised to a succession of
// conditional branches on the message ID, it is very likely that receiving
// the optimised-out message will call another behaviour on the actor, which
// is very very bad.
// This inline assembly cannot be analysed by the optimiser (and thus must be
// assumed to have side-effects), which prevents the removal of the default
// case, which in turn prevents the replacement of the switch. In addition,
// the setup in codegen_machine results in unreachable instructions being
// lowered to trapping machine instructions (e.g. ud2 on x86), which are
// guaranteed to crash the program.
// As a result, if an actor receives a message affected by this bug, the
// program will crash immediately instead of doing some crazy stuff.
// TODO: Remove this when LLVM properly supports infinite loops.
LLVMTypeRef void_fn = LLVMFunctionType(c->void_type, NULL, 0, false);
LLVMValueRef asmstr = LLVMConstInlineAsm(void_fn, "", "~{memory}", true,
false);
LLVMBuildCall(c->builder, asmstr, NULL, 0, "");
LLVMBuildUnreachable(c->builder);

codegen_finishfun(c);
}

Expand Down
26 changes: 26 additions & 0 deletions src/libponyc/codegen/host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <llvm/IRReader/IRReader.h>
#include <llvm/Linker/Linker.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Target/TargetOptions.h>

#ifdef _MSC_VER
# pragma warning(pop)
Expand All @@ -29,6 +31,30 @@

using namespace llvm;

LLVMTargetMachineRef codegen_machine(LLVMTargetRef target, pass_opt_t* opt,
bool jit)
{
Optional<Reloc::Model> reloc;

if(opt->pic || opt->library)
reloc = Reloc::PIC_;

CodeModel::Model model = jit ? CodeModel::JITDefault : CodeModel::Default;

CodeGenOpt::Level opt_level =
opt->release ? CodeGenOpt::Aggressive : CodeGenOpt::None;

TargetOptions options;
options.TrapUnreachable = true;

Target* t = reinterpret_cast<Target*>(target);

TargetMachine* m = t->createTargetMachine(opt->triple, opt->cpu,
opt->features, options, reloc, model, opt_level);

return reinterpret_cast<LLVMTargetMachineRef>(m);
}

char* LLVMGetHostCPUName()
{
return strdup(sys::getHostCPUName().str().c_str());
Expand Down

0 comments on commit 04fd226

Please sign in to comment.