Skip to content

Commit 169e15b

Browse files
author
Dwight Guth
authored
fix llvm-kompile-codegen so it optimizes code properly (#1097)
When we migrated the llvm backend optimization pipeline from being applied by `opt` to being applied by `llvm-kompile-codegen`, we made an error. The code generator was not, in fact, applying the `opt` transformation pipeline correctly. This means that `kompile -O2` will not, in fact, apply `llc -O2` to the bitcode generated by the llvm backend, leading to a significant performance regression. While this PR does not entirely fix the issue (an upstream change to the K frontend is also required), it fixes the issue within this repository. We see a roughly 1.5x speedup in runtime when `-O2` is passed to kompile. We do lose something in compilation time, however. This is normal; compiling with optimizations is more expensive, we just weren't doing it correctly before. The main changes in this pull request are threefold: 1. Convert optimizer code to new pass manager. 2. Make sure to run the middle-end optimizer. 3. Convert `TailCallElimination` and `Mem2Reg` from required to optional passes on -O0. In order to make the tail call optimization still work correctly without `TailCallElimination` manually marking tail calls as `tail`, we instead explicitly mark them as `musttail` in the IR generated by the code generator.
1 parent a9393d3 commit 169e15b

File tree

7 files changed

+83
-26
lines changed

7 files changed

+83
-26
lines changed

.github/workflows/ci-tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ make -j$(nproc) install
1717
popd
1818

1919
pushd matching
20-
mvn package
20+
mvn -B package
2121
popd
2222

2323
export PATH="$(realpath ./build/install/bin):$(realpath ./build/bin):/usr/lib/llvm-${llvm_version}/bin:$PATH"

include/kllvm/codegen/Options.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
#include "llvm/Support/CommandLine.h"
55

6-
enum class opt_level { O0, O1, O2, O3 };
7-
86
extern llvm::cl::OptionCategory codegen_lib_cat;
97

108
extern llvm::cl::opt<bool> debug;
@@ -15,7 +13,7 @@ extern llvm::cl::opt<bool> force_binary;
1513
extern llvm::cl::opt<bool> proof_hint_instrumentation;
1614
extern llvm::cl::opt<bool> proof_hint_instrumentation_slow;
1715
extern llvm::cl::opt<bool> keep_frame_pointer;
18-
extern llvm::cl::opt<opt_level> optimization_level;
16+
extern llvm::cl::opt<char> optimization_level;
1917

2018
namespace kllvm {
2119

include/kllvm/codegen/SetVisibilityHidden.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ struct set_visibility_hidden : llvm::PassInfoMixin<set_visibility_hidden> {
3131
}
3232
return llvm::PreservedAnalyses::none();
3333
}
34+
35+
// NOLINTNEXTLINE(*-identifier-naming)
36+
static bool isRequired() { return true; }
3437
};
3538

3639
} // namespace kllvm

lib/codegen/ApplyPasses.cpp

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,65 @@ namespace kllvm {
3737

3838
auto get_opt_level() {
3939
switch (optimization_level) {
40-
case opt_level::O0: return CODEGEN_OPT_LEVEL::None;
41-
case opt_level::O1: return CODEGEN_OPT_LEVEL::Less;
42-
case opt_level::O2: return CODEGEN_OPT_LEVEL::Default;
43-
case opt_level::O3: return CODEGEN_OPT_LEVEL::Aggressive;
40+
case '0': return CODEGEN_OPT_LEVEL::None;
41+
case '1': return CODEGEN_OPT_LEVEL::Less;
42+
case '2': return CODEGEN_OPT_LEVEL::Default;
43+
case '3': return CODEGEN_OPT_LEVEL::Aggressive;
44+
default:
45+
throw std::runtime_error(
46+
fmt::format("Invalid optimization level: {}", optimization_level));
4447
}
4548
}
4649

47-
void apply_kllvm_opt_passes(llvm::Module &mod, bool hidden_visibility) {
48-
auto pm = legacy::PassManager();
49-
50-
pm.add(createPromoteMemoryToRegisterPass());
51-
pm.add(createTailCallEliminationPass());
52-
if (hidden_visibility) {
53-
pm.add(new legacy_set_visibility_hidden());
50+
auto get_pass_opt_level() {
51+
switch (optimization_level) {
52+
case '0': return OptimizationLevel::O0;
53+
case '1': return OptimizationLevel::O1;
54+
case '2': return OptimizationLevel::O2;
55+
case '3': return OptimizationLevel::O3;
56+
default:
57+
throw std::runtime_error(
58+
fmt::format("Invalid optimization level: {}", optimization_level));
5459
}
60+
}
5561

56-
pm.run(mod);
62+
void apply_kllvm_opt_passes(llvm::Module &mod, bool hidden_visibility) {
63+
// Create the analysis managers.
64+
// These must be declared in this order so that they are destroyed in the
65+
// correct order due to inter-analysis-manager references.
66+
LoopAnalysisManager lam;
67+
FunctionAnalysisManager fam;
68+
CGSCCAnalysisManager cgam;
69+
ModuleAnalysisManager mam;
70+
71+
// Create the new pass manager builder.
72+
// Take a look at the PassBuilder constructor parameters for more
73+
// customization, e.g. specifying a TargetMachine or various debugging
74+
// options.
75+
PassBuilder pb;
76+
77+
// Register all the basic analyses with the managers.
78+
pb.registerModuleAnalyses(mam);
79+
pb.registerCGSCCAnalyses(cgam);
80+
pb.registerFunctionAnalyses(fam);
81+
pb.registerLoopAnalyses(lam);
82+
pb.crossRegisterProxies(lam, fam, cgam, mam);
83+
84+
// register custom passes
85+
pb.registerPipelineStartEPCallback(
86+
[hidden_visibility](
87+
llvm::ModulePassManager &pm, OptimizationLevel level) {
88+
if (hidden_visibility) {
89+
pm.addPass(set_visibility_hidden());
90+
}
91+
});
92+
93+
// Create the pass manager.
94+
ModulePassManager mpm
95+
= pb.buildPerModuleDefaultPipeline(get_pass_opt_level());
96+
97+
// Optimize the IR!
98+
mpm.run(mod, mam);
5799
}
58100

59101
void generate_object_file(llvm::Module &mod, llvm::raw_ostream &os) {

lib/codegen/CreateTerm.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,7 @@ llvm::Value *create_term::create_function_call(
795795
set_debug_loc(call);
796796
if (tailcc) {
797797
call->setCallingConv(llvm::CallingConv::Tail);
798+
call->setTailCall();
798799
}
799800
if (sret) {
800801
llvm::Attribute sret_attr
@@ -1138,7 +1139,20 @@ bool make_function(
11381139
auto *call = llvm::CallInst::Create(step, {retval}, "", current_block);
11391140
set_debug_loc(call);
11401141
call->setCallingConv(llvm::CallingConv::Tail);
1142+
call->setTailCallKind(llvm::CallInst::TCK_MustTail);
11411143
retval = call;
1144+
} else {
1145+
if (auto *call = llvm::dyn_cast<llvm::CallInst>(retval)) {
1146+
// check that musttail requirements are met:
1147+
// 1. Call is in tail position (guaranteed)
1148+
// 2. Return returns return value of call (guaranteed)
1149+
// 3. Calling convention is tailcc
1150+
// 4. Function is not sret (here approximated by checking if return type is void)
1151+
if (call->getCallingConv() == llvm::CallingConv::Tail
1152+
&& call->getType() != llvm::Type::getVoidTy(module->getContext())) {
1153+
call->setTailCallKind(llvm::CallInst::TCK_MustTail);
1154+
}
1155+
}
11421156
}
11431157
auto *ret
11441158
= llvm::ReturnInst::Create(module->getContext(), retval, current_block);
@@ -1262,6 +1276,7 @@ std::string make_apply_rule_function(
12621276
= llvm::CallInst::Create(step, args, "", creator.get_current_block());
12631277
set_debug_loc(retval);
12641278
retval->setCallingConv(llvm::CallingConv::Tail);
1279+
retval->setTailCallKind(llvm::CallInst::TCK_MustTail);
12651280
llvm::ReturnInst::Create(
12661281
module->getContext(), retval, creator.get_current_block());
12671282
return name;
@@ -1277,7 +1292,7 @@ std::string make_side_condition_function(
12771292
}
12781293
std::string name = "side_condition_" + std::to_string(axiom->get_ordinal());
12791294
if (make_function(
1280-
name, pattern, definition, module, false, false, false, axiom,
1295+
name, pattern, definition, module, true, false, false, axiom,
12811296
".sc")) {
12821297
return name;
12831298
}

lib/codegen/Decision.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,8 @@ void function_node::codegen(decision *d) {
441441
create_term creator(
442442
final_subst, d->definition_, d->current_block_, d->module_, false);
443443
auto *call = creator.create_function_call(
444-
function_, cat_, args, function_.substr(0, 5) == "hook_", false);
444+
function_, cat_, args, function_.substr(0, 5) == "hook_",
445+
is_side_condition);
445446
call->setName(name_.substr(0, max_name_length));
446447
d->store(std::make_pair(name_, type_), call);
447448

@@ -625,6 +626,7 @@ void leaf_node::codegen(decision *d) {
625626
call->setCallingConv(llvm::CallingConv::Tail);
626627

627628
if (child_ == nullptr) {
629+
call->setTailCallKind(llvm::CallInst::TCK_MustTail);
628630
llvm::ReturnInst::Create(d->ctx_, call, d->current_block_);
629631
} else {
630632
new llvm::StoreInst(

lib/codegen/Options.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,11 @@ cl::opt<bool> keep_frame_pointer(
2020
cl::desc("Keep frame pointer in compiled code for debugging purposes"),
2121
cl::cat(codegen_lib_cat));
2222

23-
cl::opt<opt_level> optimization_level(
24-
cl::desc("Choose optimization level"),
25-
cl::values(
26-
clEnumVal(opt_level::O0, "No optimizations"),
27-
clEnumVal(opt_level::O1, "Enable trivial optimizations"),
28-
clEnumVal(opt_level::O2, "Enable default optimizations"),
29-
clEnumVal(opt_level::O3, "Enable expensive optimizations")),
30-
cl::cat(codegen_lib_cat));
23+
cl::opt<char> optimization_level(
24+
"O",
25+
cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
26+
"(default = '-O0')"),
27+
cl::Prefix, cl::init('0'), cl::cat(codegen_lib_cat));
3128

3229
cl::opt<bool> debug(
3330
"debug", cl::desc("Enable debug information"), cl::ZeroOrMore,

0 commit comments

Comments
 (0)