Skip to content

Commit

Permalink
use llvm-mca as a cost model -- this implements that, but the work is…
Browse files Browse the repository at this point in the history
… unfinished and it isn't used anywhere, still work to do (google#816)
  • Loading branch information
regehr authored and manasij7479 committed Sep 14, 2021
1 parent 02a10f2 commit 7eabcbf
Show file tree
Hide file tree
Showing 15 changed files with 411 additions and 74 deletions.
38 changes: 23 additions & 15 deletions CMakeLists.txt
Expand Up @@ -262,19 +262,25 @@ add_library(souperTool STATIC
${SOUPER_TOOL_FILES}
)

set(SOUPER_CODEGEN_FILES
lib/Codegen/Codegen.cpp
lib/Codegen/MachineCost.cpp
include/souper/Codegen/Codegen.h
)

add_library(souperCodegen STATIC
${SOUPER_CODEGEN_FILES}
)

set(SOUPER_SOURCES
${SOUPER_EXTRACTOR_FILES}
${SOUPER_INST_FILES}
${SOUPER_KVSTORE_FILES}
${SOUPER_PARSER_FILES}
${SOUPER_SMTLIB2_FILES}
${SOUPER_TOOL_FILES}
${SOUPER_INFER_FILES})

set(SOUPER_CODEGEN_FILES
lib/Codegen/Codegen.cpp
include/souper/Codegen/Codegen.h
)
${SOUPER_INFER_FILES}
${SOUPER_CODEGEN_FILES})

add_library(souperPass SHARED
${KLEE_EXPR_FILES}
Expand All @@ -288,10 +294,6 @@ add_library(souperPassProfileAll SHARED
lib/Pass/Pass.cpp
)

add_library(souperCodegen SHARED
${SOUPER_CODEGEN_FILES}
)

target_compile_definitions(souperPassProfileAll PRIVATE DYNAMIC_PROFILE_ALL=1)

add_executable(clang-souper
Expand Down Expand Up @@ -346,6 +348,10 @@ add_executable(parser_tests
unittests/Parser/ParserTests.cpp
)

add_executable(codegen_tests
unittests/Codegen/CodegenTests.cpp
)

add_executable(interpreter_tests
unittests/Interpreter/InterpreterInfra.cpp
unittests/Interpreter/InterpreterTests.cpp)
Expand Down Expand Up @@ -400,26 +406,27 @@ foreach(target souperClangTool clang-souper)
set_target_properties(${target} PROPERTIES COMPILE_FLAGS "${CLANG_CXXFLAGS} ${LLVM_CXXFLAGS}")
target_include_directories(${target} PRIVATE "${LLVM_INCLUDEDIR}" ${CLANG_INCLUDEDIR})
endforeach()
foreach(target extractor_tests inst_tests parser_tests interpreter_tests bulk_tests)
foreach(target extractor_tests inst_tests parser_tests interpreter_tests bulk_tests codegen_tests)
set_target_properties(${target} PROPERTIES COMPILE_FLAGS "${GTEST_CXXFLAGS} ${LLVM_CXXFLAGS}")
target_include_directories(${target} PRIVATE "${LLVM_INCLUDEDIR}" "${GTEST_INCLUDEDIR}")
endforeach()

# static
target_link_libraries(kleeExpr ${LLVM_LIBS} ${LLVM_LDFLAGS})
target_link_libraries(souperClangTool souperExtractor souperTool ${CLANG_LIBS} ${LLVM_LIBS} ${LLVM_LDFLAGS})
target_link_libraries(souperExtractor souperParser souperKVStore souperInfer souperInst kleeExpr)
target_link_libraries(souperExtractor souperParser souperKVStore souperInfer souperInst kleeExpr souperCodegen)
target_link_libraries(souperInfer souperExtractor ${LLVM_LIBS} ${LLVM_LDFLAGS} ${Z3_LIBRARY})
target_link_libraries(souperInst ${LLVM_LIBS} ${LLVM_LDFLAGS})
target_link_libraries(souperKVStore ${HIREDIS_LIBRARY} ${LLVM_LIBS} ${LLVM_LDFLAGS})
target_link_libraries(souperParser souperInst ${LLVM_LIBS} ${LLVM_LDFLAGS} ${ALIVE_LIBRARY})
target_link_libraries(souperSMTLIB2 ${LLVM_LIBS} ${LLVM_LDFLAGS})
target_link_libraries(souperTool souperExtractor souperSMTLIB2)
target_link_libraries(souperCodegen ${LLVM_LIBS} ${LLVM_LDFLAGS})

# dynamic
target_link_libraries(souperCodegen ${PASS_LDFLAGS})
target_link_libraries(souperPass souperCodegen ${PASS_LDFLAGS} ${HIREDIS_LIBRARY} ${ALIVE_LIBRARY} ${Z3_LIBRARY})
target_link_libraries(souperPassProfileAll souperCodegen ${PASS_LDFLAGS} ${HIREDIS_LIBRARY} ${ALIVE_LIBRARY} ${Z3_LIBRARY})
target_link_libraries(souperPass ${PASS_LDFLAGS} ${HIREDIS_LIBRARY} ${ALIVE_LIBRARY} ${Z3_LIBRARY})
target_link_libraries(souperPassProfileAll ${PASS_LDFLAGS} ${HIREDIS_LIBRARY} ${ALIVE_LIBRARY} ${Z3_LIBRARY})

# executables
target_link_libraries(souper souperExtractor souperKVStore souperParser souperSMTLIB2 souperTool kleeExpr ${HIREDIS_LIBRARY} ${ALIVE_LIBRARY} ${Z3_LIBRARY})
Expand All @@ -435,6 +442,7 @@ target_link_libraries(souper2llvm souperParser souperCodegen)
target_link_libraries(extractor_tests souperExtractor souperParser ${GTEST_LIBS} ${ALIVE_LIBRARY})
target_link_libraries(inst_tests souperInfer souperPass souperInst souperExtractor ${GTEST_LIBS} ${ALIVE_LIBRARY})
target_link_libraries(parser_tests souperParser ${GTEST_LIBS} ${ALIVE_LIBRARY})
target_link_libraries(codegen_tests souperCodegen souperInst ${GTEST_LIBS} ${ALIVE_LIBRARY})
target_link_libraries(interpreter_tests souperInfer souperInst ${GTEST_LIBS} ${ALIVE_LIBRARY})
target_link_libraries(bulk_tests souperInfer souperInst ${GTEST_LIBS} ${ALIVE_LIBRARY} ${Z3_LIBRARY})

Expand Down Expand Up @@ -464,7 +472,7 @@ configure_file(

add_custom_target(check
COMMAND ${CMAKE_BINARY_DIR}/run_lit
DEPENDS extractor_tests inst_tests parser-test parser_tests profileRuntime souper souper-check souper-interpret souperPass souper2llvm souperPassProfileAll count-insts interpreter_tests bulk_tests
DEPENDS extractor_tests inst_tests parser-test parser_tests profileRuntime souper souper-check souper-interpret souperPass souper2llvm souperPassProfileAll count-insts interpreter_tests bulk_tests codegen_tests
USES_TERMINAL)

# we want assertions even in release mode!
Expand Down
17 changes: 17 additions & 0 deletions include/souper/Codegen/Codegen.h
Expand Up @@ -16,13 +16,17 @@
#define SOUPER_CODEGEN_CODEGEN_H

#include "souper/Inst/Inst.h"
#include "souper/Parser/Parser.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/Verifier.h"
#include <map>

#include "llvm/Support/MemoryBuffer.h"

namespace souper {

class Codegen {
Expand All @@ -47,6 +51,19 @@ class Codegen {
llvm::Value *getValue(Inst *I);
};

// If there are no errors, the function returns false. If an error is found,
// a message describing the error is written to OS (if non-null) and true is
// returned.
bool genModule(InstContext &IC, Inst *I, llvm::Module &Module);

struct BackendCost {
std::vector<int> C;
};

void getBackendCost(InstContext &IC, Inst *I, BackendCost &BC);

bool compareCosts(const BackendCost &C1, const BackendCost &C2);

} // namespace souper

#endif // SOUPER_CODEGEN_CODEGEN_H
2 changes: 2 additions & 0 deletions include/souper/Inst/Inst.h
Expand Up @@ -269,6 +269,7 @@ class InstContext {
llvm::APInt DemandedBits, bool Available);

std::vector<Inst *> getVariables() const;
std::vector<Inst *> getVariablesFor(Inst *Root) const;
};

struct SynthesisContext {
Expand All @@ -283,6 +284,7 @@ struct SynthesisContext {
};

int cost(Inst *I, bool IgnoreDepsWithExternalUses = false);
int backendCost(Inst *I, bool IgnoreDepsWithExternalUses = false);
int countHelper(Inst *I, std::set<Inst *> &Visited);
int instCount(Inst *I);
int benefit(Inst *LHS, Inst *RHS);
Expand Down
58 changes: 58 additions & 0 deletions lib/Codegen/Codegen.cpp
Expand Up @@ -321,4 +321,62 @@ llvm::Value *Codegen::getValue(Inst *I) {
Inst::getKindName(I->K) + " in Codegen::getValue()");
}

static std::vector<llvm::Type *>
GetInputArgumentTypes(const InstContext &IC, llvm::LLVMContext &Context, Inst *Root) {
const std::vector<Inst *> AllVariables = IC.getVariablesFor(Root);

std::vector<llvm::Type *> ArgTypes;
ArgTypes.reserve(AllVariables.size());
for (const Inst *const Var : AllVariables) {
llvm::errs() << "arg with width " << Var->Width << " and number " << Var->Number << "\n";
ArgTypes.emplace_back(Type::getIntNTy(Context, Var->Width));
}

return ArgTypes;
}

static std::map<Inst *, Value *> GetArgsMapping(const InstContext &IC,
Function *F, Inst *Root) {
std::map<Inst *, Value *> Args;

const std::vector<Inst *> AllVariables = IC.getVariablesFor(Root);
for (auto zz : llvm::zip(AllVariables, F->args()))
Args[std::get<0>(zz)] = &(std::get<1>(zz));

return Args;
};

/// If there are no errors, the function returns false. If an error is found,
/// a message describing the error is written to OS (if non-null) and true is
/// returned.
bool genModule(InstContext &IC, souper::Inst *I, llvm::Module &Module) {
llvm::LLVMContext &Context = Module.getContext();
const std::vector<llvm::Type *> ArgTypes = GetInputArgumentTypes(IC, Context, I);
const auto FT = llvm::FunctionType::get(
/*Result=*/Codegen::GetInstReturnType(Context, I),
/*Params=*/ArgTypes, /*isVarArg=*/false);

Function *F = Function::Create(FT, Function::ExternalLinkage, "fun", &Module);

const std::map<Inst *, Value *> Args = GetArgsMapping(IC, F, I);

BasicBlock *BB = BasicBlock::Create(Context, "entry", F);

llvm::IRBuilder<> Builder(Context);
Builder.SetInsertPoint(BB);

Value *RetVal = Codegen(Context, &Module, Builder, /*DT*/ nullptr,
/*ReplacedInst*/ nullptr, Args)
.getValue(I);

Builder.CreateRet(RetVal);

// Validate the generated code, checking for consistency.
if (verifyFunction(*F, &llvm::errs()))
return true;
if (verifyModule(Module, &llvm::errs()))
return true;
return false;
}

} // namespace souper
173 changes: 173 additions & 0 deletions lib/Codegen/MachineCost.cpp
@@ -0,0 +1,173 @@
// Copyright 2014 The Souper Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "souper/Codegen/Codegen.h"
#include "souper/Inst/Inst.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Value.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/SmallVectorMemoryBuffer.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include <map>

#define DEBUG_TYPE "souper"

using namespace llvm;

namespace souper {

void optimizeModule(llvm::Module &M) {
llvm::LoopAnalysisManager LAM;
llvm::FunctionAnalysisManager FAM;
llvm::CGSCCAnalysisManager CGAM;
llvm::ModuleAnalysisManager MAM;

llvm::PassBuilder PB;
PB.registerModuleAnalyses(MAM);
PB.registerCGSCCAnalyses(CGAM);
PB.registerFunctionAnalyses(FAM);
PB.registerLoopAnalyses(LAM);
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);

llvm::FunctionPassManager FPM =
PB.buildFunctionSimplificationPipeline(llvm::PassBuilder::OptimizationLevel::O2,
ThinOrFullLTOPhase::None);
llvm::ModulePassManager MPM;
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
MPM.run(M, MAM);
}

long getCodeSize(Module &M, TargetMachine *TM) {
M.setDataLayout(TM->createDataLayout());
SmallVector<char, 256> DotO;
raw_svector_ostream dest(DotO);

legacy::PassManager pass;
if (TM->addPassesToEmitFile(pass, dest, nullptr, CGFT_ObjectFile)) {
errs() << "Target machine can't emit a file of this type";
report_fatal_error("oops");
}
pass.run(M);

SmallVectorMemoryBuffer Buf(std::move(DotO));
auto ObjOrErr = object::ObjectFile::createObjectFile(Buf);
if (!ObjOrErr)
report_fatal_error("createObjectFile() failed");
object::ObjectFile *OF = ObjOrErr.get().get();
auto SecList = OF->sections();
long Size = 0;
for (auto &S : SecList) {
if (S.isText())
Size += S.getSize();
}
if (Size > 0)
return Size;
else
report_fatal_error("no text segment found");
}

struct TargetInfo {
std::string Trip, CPU;
};

std::vector<TargetInfo> Targets {
{ "x86_64", "skylake" },
{ "aarch64", "apple-a12" },
};

bool Init = false;

void getBackendCost(InstContext &IC, souper::Inst *I, BackendCost &BC) {
// TODO is this better than just forcing all clients of this code to
// do the init themselves?
if (!Init) {
InitializeAllTargetInfos();
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmParsers();
InitializeAllAsmPrinters();
Init = true;
}

llvm::LLVMContext C;
llvm::Module M("", C);
if (genModule(IC, I, M))
llvm::report_fatal_error("codegen error in getBackendCost()");

optimizeModule(M);

llvm::errs() << M;

BackendCost Cost;
for (auto &T : Targets) {
std::string Error;
auto Target = TargetRegistry::lookupTarget(T.Trip, Error);
if (!Target) {
errs() << Error;
report_fatal_error("can't lookup target");
}

auto Features = "";
TargetOptions Opt;
auto RM = Optional<Reloc::Model>();
auto TM = Target->createTargetMachine(T.Trip, T.CPU, Features, Opt, RM);

Cost.C.push_back(getCodeSize(M, TM));
}

llvm::errs() << "cost vector: ";
for (auto I : Cost.C) {
llvm::errs() << I << " ";
}
llvm::errs() << "\n";
}

int threeWayCompare(int A, int B) {
if (A < B)
return -1;
if (A > B)
return 1;
return 0;
}

// "The value returned indicates whether the element passed as first
// argument is considered to go before the second"
bool compareCosts(const BackendCost &C1, const BackendCost &C2) {
assert(C1.C.size() == C2.C.size());

int Count = 0;
for (int i = 0; i < C1.C.size(); ++i)
Count += threeWayCompare(C1.C[i], C2.C[i]);
if (Count < 0)
return true;
if (Count > 0)
return false;

// break ties using souper cost?
// break final ties how? we want a canonical winner for all cases
// FIXME -- not finished
return false;
}

} // namespace souper

0 comments on commit 7eabcbf

Please sign in to comment.