Skip to content

Commit

Permalink
make llvm backend do better dce and dae (#1144)
Browse files Browse the repository at this point in the history
Previously, the llvm optimization passes weren't terribly effective at
removing dead code and dead function arguments. We add some custom llvm
passes that make this work a bit more effectively.

There are two passes added:

* RemoveDeadKFunctions deletes function call instructions which call
side-effecting functions that are "effectively" pure, i.e., they exist
only to construct pure terms and return them.
* MustTailDeadArgElimination is a minorly modified version of the
deadargelim LLVM pass which fixes a bug preventing it from applying dead
argument elimination to musttail tail calls.
  • Loading branch information
dwightguth authored Sep 13, 2024
1 parent d7503bd commit 5ecfcc0
Show file tree
Hide file tree
Showing 18 changed files with 1,571 additions and 89 deletions.
7 changes: 3 additions & 4 deletions bin/llvm-kompile-clang
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ if ! $save_temps; then
fi

if [[ "$OSTYPE" == "darwin"* ]]; then
set_visibility_hidden="$LIBDIR/libSetVisibilityHidden.dylib"
passes="$LIBDIR/libKLLVMPass.dylib"
else
set_visibility_hidden="$LIBDIR/libSetVisibilityHidden.so"
passes="$LIBDIR/libKLLVMPass.so"
fi

# On macOS, we get libunwind supplied as part of the developer tools in the OS,
Expand All @@ -176,7 +176,6 @@ fi
if [ "$main" != "python_ast" ]; then
if [ "$lto" = "lto" ]; then
flags+=("-flto")
files=("$LIBDIR"/llvm/*.ll)
else
files=()
if $compile; then
Expand All @@ -186,7 +185,7 @@ if [ "$main" != "python_ast" ]; then
fi
if $visibility_hidden; then
modhidden="$tmpdir/hidden.bc"
run @OPT@ "$modopt" -load-pass-plugin "$set_visibility_hidden" -set-visibility-hidden -o "$modhidden"
run @OPT@ "$modopt" -load-pass-plugin "$passes" -set-visibility-hidden -o "$modhidden"
modopt="$modhidden"
fi
run @LLC@ \
Expand Down
1 change: 1 addition & 0 deletions include/kllvm/ast/attribute_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class attribute_set {
Functional,
Hook,
Idem,
Impure,
Label,
Left,
Location,
Expand Down
3 changes: 2 additions & 1 deletion include/kllvm/codegen/CreateTerm.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ class create_term {
*/
llvm::Value *create_function_call(
std::string const &name, value_type return_cat,
std::vector<llvm::Value *> const &args, bool sret, bool tailcc);
std::vector<llvm::Value *> const &args, bool sret, bool tailcc,
bool impure);

[[nodiscard]] llvm::BasicBlock *get_current_block() const {
return current_block_;
Expand Down
160 changes: 160 additions & 0 deletions include/kllvm/codegen/MustTailDeadArgElimination.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
//===- DeadArgumentElimination.h - Eliminate Dead Args ----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Minor modification made by Pi Squared Inc to support tailcc musttail calls.
//
//===----------------------------------------------------------------------===//
//
// This pass deletes dead arguments from internal functions. Dead argument
// elimination removes arguments which are directly dead, as well as arguments
// only passed into function calls as dead arguments of other functions. This
// pass also deletes dead return values in a similar way.
//
// This pass is often useful as a cleanup pass to run after aggressive
// interprocedural passes, which add possibly-dead arguments or return values.
//
//===----------------------------------------------------------------------===//

// NOLINTBEGIN

#ifndef LLVM_TRANSFORMS_IPO_DEADARGUMENTELIMINATION_H
#define LLVM_TRANSFORMS_IPO_DEADARGUMENTELIMINATION_H

#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include <map>
#include <set>
#include <string>
#include <tuple>

using namespace llvm;

namespace llvm {

class Module;
class Use;
class Value;

} // namespace llvm

namespace kllvm {

/// Eliminate dead arguments (and return values) from functions.
class DeadArgumentEliminationPass
: public PassInfoMixin<DeadArgumentEliminationPass> {
public:
#if LLVM_VERSION_MAJOR == 16
/// Struct that represents (part of) either a return value or a function
/// argument. Used so that arguments and return values can be used
/// interchangeably.
struct RetOrArg {
Function const *F;
unsigned Idx;
bool IsArg;

RetOrArg(Function const *F, unsigned Idx, bool IsArg)
: F(F)
, Idx(Idx)
, IsArg(IsArg) { }

/// Make RetOrArg comparable, so we can put it into a map.
bool operator<(RetOrArg const &O) const {
return std::tie(F, Idx, IsArg) < std::tie(O.F, O.Idx, O.IsArg);
}

/// Make RetOrArg comparable, so we can easily iterate the multimap.
bool operator==(RetOrArg const &O) const {
return F == O.F && Idx == O.Idx && IsArg == O.IsArg;
}

std::string getDescription() const {
return (Twine(IsArg ? "Argument #" : "Return value #") + Twine(Idx)
+ " of function " + F->getName())
.str();
}
};

/// During our initial pass over the program, we determine that things are
/// either alive or maybe alive. We don't mark anything explicitly dead (even
/// if we know they are), since anything not alive with no registered uses
/// (in Uses) will never be marked alive and will thus become dead in the end.
enum Liveness { Live, MaybeLive };

DeadArgumentEliminationPass(bool ShouldHackArguments = false)
: ShouldHackArguments(ShouldHackArguments) { }
#endif
PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
#if LLVM_VERSION_MAJOR == 16
/// Convenience wrapper
RetOrArg createRet(Function const *F, unsigned Idx) {
return RetOrArg(F, Idx, false);
}

/// Convenience wrapper
RetOrArg createArg(Function const *F, unsigned Idx) {
return RetOrArg(F, Idx, true);
}

using UseMap = std::multimap<RetOrArg, RetOrArg>;

/// This maps a return value or argument to any MaybeLive return values or
/// arguments it uses. This allows the MaybeLive values to be marked live
/// when any of its users is marked live.
/// For example (indices are left out for clarity):
/// - Uses[ret F] = ret G
/// This means that F calls G, and F returns the value returned by G.
/// - Uses[arg F] = ret G
/// This means that some function calls G and passes its result as an
/// argument to F.
/// - Uses[ret F] = arg F
/// This means that F returns one of its own arguments.
/// - Uses[arg F] = arg G
/// This means that G calls F and passes one of its own (G's) arguments
/// directly to F.
UseMap Uses;

using LiveSet = std::set<RetOrArg>;
using LiveFuncSet = std::set<Function const *>;

/// This set contains all values that have been determined to be live.
LiveSet LiveValues;

/// This set contains all values that are cannot be changed in any way.
LiveFuncSet LiveFunctions;

using UseVector = SmallVector<RetOrArg, 5>;

/// This allows this pass to do double-duty as the dead arg hacking pass
/// (used only by bugpoint).
bool ShouldHackArguments = false;

private:
Liveness markIfNotLive(RetOrArg Use, UseVector &MaybeLiveUses);
Liveness
surveyUse(Use const *U, UseVector &MaybeLiveUses, unsigned RetValNum = -1U);
Liveness surveyUses(Value const *V, UseVector &MaybeLiveUses);

void surveyFunction(Function const &F);
bool isLive(RetOrArg const &RA);
void
markValue(RetOrArg const &RA, Liveness L, UseVector const &MaybeLiveUses);
void markLive(RetOrArg const &RA);
void markLive(Function const &F);
void propagateLiveness(RetOrArg const &RA);
bool removeDeadStuffFromFunction(Function *F);
bool deleteDeadVarargs(Function &F);
bool removeDeadArgumentsFromCallers(Function &F);
#endif
};

} // namespace kllvm

#endif // LLVM_TRANSFORMS_IPO_DEADARGUMENTELIMINATION_H

// NOLINTEND
45 changes: 45 additions & 0 deletions include/kllvm/codegen/RemoveDeadKFunctions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#ifndef REMOVE_DEAD_K_FUNCTIONS_H
#define REMOVE_DEAD_K_FUNCTIONS_H

#include "llvm/IR/Function.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Pass.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

namespace kllvm {

bool run_remove_dead_k_functions(llvm::Function &f, TargetLibraryInfo *tli);

struct legacy_remove_dead_k_functions : llvm::FunctionPass {
// NOLINTNEXTLINE(*-identifier-naming)
static char ID;
legacy_remove_dead_k_functions()
: llvm::FunctionPass(ID) { }
bool runOnFunction(llvm::Function &f) override {
TargetLibraryInfo *tli
= &getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(f);
return run_remove_dead_k_functions(f, tli);
}
};

struct remove_dead_k_functions : llvm::PassInfoMixin<remove_dead_k_functions> {
static llvm::PreservedAnalyses
run(llvm::Function &f, llvm::FunctionAnalysisManager &am) {
if (!run_remove_dead_k_functions(
f, &am.getResult<TargetLibraryAnalysis>(f))) {
return llvm::PreservedAnalyses::all();
}
llvm::PreservedAnalyses pa;
pa.preserveSet<llvm::CFGAnalyses>();
return pa;
}
};

} // namespace kllvm

#endif
2 changes: 1 addition & 1 deletion lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ add_subdirectory(ast)
add_subdirectory(binary)
add_subdirectory(codegen)
add_subdirectory(printer)
add_subdirectory(set-visibility-hidden)
add_subdirectory(passes)

separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
1 change: 1 addition & 0 deletions lib/ast/attribute_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ std::unordered_map<attribute_set::key, std::string> const &attribute_table() {
{attribute_set::key::Functional, "functional"},
{attribute_set::key::Hook, "hook"},
{attribute_set::key::Idem, "idem"},
{attribute_set::key::Impure, "impure"},
{attribute_set::key::Label, "label"},
{attribute_set::key::Left, "left"},
{attribute_set::key::Location,
Expand Down
12 changes: 12 additions & 0 deletions lib/codegen/ApplyPasses.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include <kllvm/codegen/ApplyPasses.h>
#include <kllvm/codegen/MustTailDeadArgElimination.h>
#include <kllvm/codegen/Options.h>
#include <kllvm/codegen/RemoveDeadKFunctions.h>
#include <kllvm/codegen/SetVisibilityHidden.h>

#include "runtime/alloc_cpp.h"
Expand Down Expand Up @@ -93,6 +95,16 @@ void apply_kllvm_opt_passes(llvm::Module &mod, bool hidden_visibility) {
pm.addPass(set_visibility_hidden());
}
});
pb.registerScalarOptimizerLateEPCallback(
[](llvm::FunctionPassManager &pm, OptimizationLevel level) {
pm.addPass(remove_dead_k_functions());
});
pb.registerOptimizerEarlyEPCallback(
[](llvm::ModulePassManager &pm, OptimizationLevel level) {
pm.addPass(DeadArgumentEliminationPass());
pm.addPass(
llvm::createModuleToFunctionPassAdaptor(remove_dead_k_functions()));
});

// Create the pass manager.
ModulePassManager mpm
Expand Down
16 changes: 14 additions & 2 deletions lib/codegen/CreateTerm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,13 @@ llvm::Value *create_term::create_function_call(
auto *return_sort = dynamic_cast<kore_composite_sort *>(
pattern->get_constructor()->get_sort().get());
auto return_cat = return_sort->get_category(definition_);
auto const &att = definition_->get_symbol_declarations()
.at(pattern->get_constructor()->get_name())
->attributes();

bool impure = att.contains(attribute_set::key::Impure)
|| !att.contains(attribute_set::key::Total);

int i = 0;
for (auto const &sort : pattern->get_constructor()->get_arguments()) {
auto *concrete_sort = dynamic_cast<kore_composite_sort *>(sort.get());
Expand Down Expand Up @@ -832,12 +839,13 @@ llvm::Value *create_term::create_function_call(
current_block_ = e.function_event_post(current_block_);
}

return create_function_call(name, return_cat, args, sret, tailcc);
return create_function_call(name, return_cat, args, sret, tailcc, impure);
}

llvm::Value *create_term::create_function_call(
std::string const &name, value_type return_cat,
std::vector<llvm::Value *> const &args, bool sret, bool tailcc) {
std::vector<llvm::Value *> const &args, bool sret, bool tailcc,
bool impure) {
llvm::Type *return_type = getvalue_type(return_cat, module_);
std::vector<llvm::Type *> types;
bool collection = false;
Expand Down Expand Up @@ -872,6 +880,9 @@ llvm::Value *create_term::create_function_call(
llvm::FunctionType *func_type
= llvm::FunctionType::get(return_type, types, false);
llvm::Function *func = get_or_insert_function(module_, name, func_type);
if (!impure) {
func->addFnAttr("kllvm-pure");
}

auto *call = llvm::CallInst::Create(func, real_args, "", current_block_);
set_debug_loc(call);
Expand Down Expand Up @@ -1173,6 +1184,7 @@ bool make_function(
llvm::FunctionType *func_type
= llvm::FunctionType::get(return_type, param_types, false);
llvm::Function *apply_rule = get_or_insert_function(module, name, func_type);
apply_rule->setLinkage(llvm::GlobalValue::InternalLinkage);
init_debug_axiom(axiom->attributes());
std::string debug_name = name;
if (axiom->attributes().contains(attribute_set::key::Label)) {
Expand Down
9 changes: 8 additions & 1 deletion lib/codegen/Decision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ void function_node::codegen(decision *d) {
final_subst, d->definition_, d->current_block_, d->module_, false);
auto *call = creator.create_function_call(
function_, cat_, args, function_.substr(0, 5) == "hook_",
is_side_condition);
is_side_condition, false);
call->setName(name_.substr(0, max_name_length));
d->store(std::make_pair(name_, type_), call);

Expand Down Expand Up @@ -806,6 +806,13 @@ void make_eval_or_anywhere_function(
// have one correct version of the function body after code generation
// finishes.
match_func->deleteBody();
auto const &att = definition->get_symbol_declarations()
.at(function->get_name())
->attributes();
if (!att.contains(attribute_set::key::Impure)
&& att.contains(attribute_set::key::Total)) {
match_func->addFnAttr("kllvm-pure");
}
[[maybe_unused]] kore_symbol_declaration *symbol_decl
= definition->get_symbol_declarations().at(function->get_name());
init_debug_axiom(symbol_decl->attributes());
Expand Down
23 changes: 23 additions & 0 deletions lib/passes/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
add_library(KLLVMPassInternal
SetVisibilityHidden.cpp
RemoveDeadKFunctions.cpp
MustTailDeadArgElimination.cpp
PluginInfo.cpp
)

add_library(KLLVMPass MODULE
SetVisibilityHidden.cpp
RemoveDeadKFunctions.cpp
MustTailDeadArgElimination.cpp
PluginInfo.cpp
)

install(
TARGETS KLLVMPassInternal KLLVMPass
LIBRARY DESTINATION lib/kllvm
)

llvm_config(KLLVMPass
USE_SHARED true
core irreader passes
)
Loading

0 comments on commit 5ecfcc0

Please sign in to comment.