Works well enough to load and optimize the simple shader; doesn't run it though

This commit is contained in:
2022-05-25 19:33:07 +02:00
parent 673e00456e
commit a0b0ceb6d5
8 changed files with 909 additions and 34 deletions

View File

@@ -21,11 +21,12 @@ add_definitions(${LLVM_DEFINITIONS})
add_executable(bluebell
src/bluebell.cpp src/bluebell.hpp
src/pre-compiler.cpp src/pre-compiler.hpp src/compiler.cpp src/compiler.hpp)
src/pre-compiler.cpp src/pre-compiler.hpp src/compiler.cpp src/compiler.hpp src/spv_meta.cpp src/spv_meta.hpp)
#target_link_libraries(bluebell PkgConfig::shaderc)
llvm_map_components_to_libnames(llvm_libs core)
#print(${LLVM_AVAILABLE_LIBS})
llvm_map_components_to_libnames(llvm_libs core native passes nativecodegen all)
target_link_libraries(bluebell shaderc_shared Boost::boost Boost::log ${llvm_libs})

View File

@@ -8,6 +8,7 @@
#include "bluebell.hpp"
#include "pre-compiler.hpp"
#include "compiler.hpp"
#include "spv_meta.hpp"
const char* SHADER_PREFIX = "#version 320 es\n\
precision highp float;\
@@ -31,6 +32,9 @@ const char* SHADER_SUFFIX = "void main() { \
}";
int main(int argc, char** argv) {
build_spv_meta();
InitLLVM();
std::stringstream shader_src;
std::ifstream input(argv[1]);
@@ -38,6 +42,7 @@ int main(int argc, char** argv) {
shader_src << input.rdbuf();
shader_src << SHADER_SUFFIX;
// std::cout << shader_src.str();
auto shader = pre_compile_shader(shader_src.str());

View File

@@ -6,24 +6,38 @@
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <boost/log/trivial.hpp>
#include <boost/format.hpp>
#include <spirv-tools/libspirv.hpp>
#include <spirv/1.0/spirv.hpp11>
#include <spirv/1.0/GLSL.std.450.h>
#include <sstream>
#include <iostream>
#include <utility>
#include "spv_meta.hpp"
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
class SpirvType {
protected:
llvm::Type *llvm = nullptr;
std::string name;
virtual llvm::Type *build_llvm_type_impl(llvm::LLVMContext& ctx) = 0;
public:
virtual bool is_composite() const { return false; }
virtual bool is_vector() const { return false; }
virtual std::shared_ptr<SpirvType> inner_type() const { return nullptr; }
void set_name(std::string& new_name) { name = new_name; }
virtual ~SpirvType() = default;
llvm::Type *get_llvm_type() const { return llvm; }
@@ -89,6 +103,9 @@ public:
return inner;
}
bool is_composite() const override {return true; }
bool is_vector() const override { return true; }
explicit SpirvSequenceType(std::shared_ptr<SpirvType> &&inner): inner(inner) {}
explicit SpirvSequenceType(std::shared_ptr<SpirvType> &inner): inner(inner) {}
};
@@ -139,7 +156,8 @@ class SpirvStructType: public SpirvType {
public:
std::vector<std::shared_ptr<SpirvType>> members;
explicit SpirvStructType(std::vector<std::shared_ptr<SpirvType>> members) : members(std::move(members)) {}
explicit SpirvStructType(std::vector<std::shared_ptr<SpirvType>> members)
: members(std::move(members)) {}
llvm::Type * build_llvm_type_impl(llvm::LLVMContext &ctx) override {
std::vector<llvm::Type*> inner_types;
@@ -148,6 +166,10 @@ public:
inner_types.push_back(inner->get_llvm_type());
}
auto ret = llvm::StructType::create(ctx, llvm::ArrayRef<llvm::Type*>(inner_types));
if (!name.empty()) {
ret->setName(name);
}
return ret;
}
};
@@ -186,6 +208,20 @@ private:
}
};
class SpirvGlobalRef: public SpirvType {
public:
explicit SpirvGlobalRef(llvm::Type* globalType) {
llvm = globalType;
}
protected:
llvm::Type *build_llvm_type_impl(llvm::LLVMContext &ctx) override {
BOOST_LOG_TRIVIAL(error) << " Tried to recreate the global type";
return llvm;
}
};
std::string decode_string_arg(const uint32_t* args) {
std::string result;
for (int i = 0;; i++) {
@@ -233,13 +269,19 @@ struct Constant {
enum class Type {
U32, U64, I32, I64,
F32, F64,
Subclass,
} type;
union {
uint64_t ival;
double fval;
};
protected:
explicit Constant(Type type): type(Type::U32) {
fval = 0;
ival = 0;
}
public:
Constant(uint32_t val): type(Type::U32), ival(val) {}
Constant(uint64_t val): type(Type::U64), ival(val) {}
@@ -250,16 +292,62 @@ struct Constant {
Constant(Type type, uint64_t val): type(type), ival(val) {}
Constant(Type type, double val): type(type), fval(val) {}
Constant(): type(Type::I32), ival(0) {}
};
#pragma clang diagnostic pop
class Variable {
public:
void get_llvm_var() {
virtual ~Constant() = default;
virtual llvm::Constant *get_llvm_const(llvm::LLVMContext &ctx) {
switch (type) {
case Type::U32:return llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), ival, false);
case Type::U64:return llvm::ConstantInt::get(llvm::Type::getInt64Ty(ctx), ival, false);
case Type::I32:return llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), ival, true);
case Type::I64:return llvm::ConstantInt::get(llvm::Type::getInt64Ty(ctx), ival, true);
case Type::F32:return llvm::ConstantFP::get(llvm::Type::getFloatTy(ctx), fval);
case Type::F64:return llvm::ConstantFP::get(llvm::Type::getDoubleTy(ctx), fval);
case Type::Subclass:
BOOST_LOG_TRIVIAL(error) << "This should have been handled by the subclass";
return nullptr;
}
}
};
struct ConstComposite: public Constant {
llvm::Constant *val;
explicit ConstComposite(llvm::Constant* val): Constant(Type::Subclass), val(val) {}
llvm::Constant * get_llvm_const(llvm::LLVMContext &ctx) override {
return val;
}
};
#pragma clang diagnostic pop
struct FunctionContext;
struct Global {
std::shared_ptr<SpirvType> var_type;
int element_no;
// TODO: Collect binding info
};
struct FunctionContext {
std::map<uint32_t, llvm::Value*> values;
std::map<uint32_t, llvm::Value*> speculative_values;
llvm::Function *function;
int cur_arg;
bool in_prologue;
std::map<uint32_t, llvm::BasicBlock*> basic_blocks;
FunctionContext(
struct CompilerImpl &compiler,
llvm::FunctionType *type,
uint32_t id
);
void gen_prologue(struct CompilerImpl &compiler);
};
struct CompilerImpl {
std::unique_ptr<llvm::LLVMContext> ctx;
std::unique_ptr<llvm::Module> module;
@@ -267,16 +355,24 @@ struct CompilerImpl {
std::map<uint32_t, spv_ext_inst_type_t> ext_insts;
std::map<uint32_t, std::shared_ptr<SpirvType>> types;
std::map<uint32_t, Constant> constants;
std::map<uint32_t, std::shared_ptr<Constant>> constants;
std::map<std::string, std::shared_ptr<EntryPoint>> entry_points;
std::map<uint32_t, Variable> globals;
std::map<uint32_t, Variable> function_vars;
std::map<uint32_t, Global> globals;
std::map<uint32_t, std::shared_ptr<FunctionContext>> functions;
llvm::StructType* global_type;
std::shared_ptr<SpirvType> global_type_spv;
std::shared_ptr<FunctionContext> cur_function;
// decorations
std::map<uint32_t, std::string> value_names;
public:
CompilerImpl() {
ctx = std::make_unique<llvm::LLVMContext>();
module = std::make_unique<llvm::Module>("shader", *ctx);
builder = std::make_unique<llvm::IRBuilder<>>(*ctx);
global_type = llvm::StructType::create(*ctx, "globals");
global_type_spv = std::make_shared<SpirvGlobalRef>(global_type->getPointerTo());
}
bool process_module(std::vector<uint32_t> &spv_module) {
@@ -297,6 +393,21 @@ public:
return true;
}
void build_global_type() {
std::vector<llvm::Type*> members;
for (auto &global: globals) {
members.push_back(global.second.var_type->get_llvm_type()->getPointerElementType());
}
global_type->setBody(members);
}
void finish_tgv() {
for (auto &type: types) {
type.second->build_llvm_type(*ctx);
}
build_global_type();
}
spv_result_t
handle_spv_header(spv_endianness_t endianness, uint32_t magic, uint32_t version,
uint32_t generator,
@@ -312,7 +423,7 @@ public:
std::string format_insn(const spv_parsed_instruction_t* insn) {
std::stringstream msg_stream;
msg_stream << "Opcode(" << insn->opcode << "):";
msg_stream << "Opcode(" << insn_names[insn->opcode] << "/" << insn->opcode << "):";
for (int i = 0; i < insn->num_operands; i++) {
auto operand = &insn->operands[i];
@@ -330,6 +441,8 @@ public:
spv_result_t handle_spv_insn(const spv_parsed_instruction_t *insn) {
uint32_t rid;
uint32_t rty;
bool has_rid = false;
bool has_rty = false;
uint32_t first_operand = insn->num_operands;
auto idata = insn->words;
@@ -337,8 +450,10 @@ public:
auto operand = &insn->operands[i];
if (operand->type == SPV_OPERAND_TYPE_RESULT_ID) {
rid = OP_WORD(i);
has_rid = true;
} else if (operand->type == SPV_OPERAND_TYPE_TYPE_ID) {
rty = OP_WORD(i);
has_rty = true;
} else {
first_operand = i;
break;
@@ -392,6 +507,20 @@ public:
break;
}
//endregion
//region 3.42.2 Debug insns
case Op::OpSource:
case Op::OpSourceContinued:
case Op::OpSourceExtension:
case Op::OpMemberName:
case Op::OpString:
case Op::OpLine:
case Op::OpNoLine:
break;
case Op::OpName: {
value_names[OP_WORD(0)] = decode_string_arg(OP_BYTES(1));
break;
}
//endregion
//region Annotations
case Op::OpDecorate: {
// TODO: save these for bindings
@@ -427,7 +556,7 @@ public:
break;
}
case Op::OpTypeArray: {
types[rid] = std::make_shared<SpirvArrayType>(types[OP_WORD(1)], constants[OP_WORD(2)].ival);
types[rid] = std::make_shared<SpirvArrayType>(types[OP_WORD(1)], constants[OP_WORD(2)]->ival);
break;
}
// Op::OpTypeRuntimeArray: {}
@@ -437,6 +566,10 @@ public:
members.push_back(types[OP_WORD(i)]);
}
types[rid] = std::make_shared<SpirvStructType>(std::move(members));
auto name_it = value_names.find(rid);
if (name_it != value_names.end()) {
types[rid]->set_name(name_it->second);
}
break;
}
case Op::OpTypePointer: {
@@ -446,6 +579,7 @@ public:
}
case Op::OpTypeFunction: {
std::vector<std::shared_ptr<SpirvType>> args;
args.push_back(global_type_spv);
for (int i = 2; i < insn->num_operands; i++) {
args.push_back(types[OP_WORD(i)]);
}
@@ -453,13 +587,47 @@ public:
break;
}
//endregion
//region Constants
//region 3.42.4 Extension instructions
case Op::OpExtInst: {
auto ext_set = ext_insts[OP_WORD(2)];
auto ext_inst = OP_WORD(3);
switch (ext_set) {
case SPV_EXT_INST_TYPE_GLSL_STD_450: {
switch (GLSLstd450(ext_inst)) {
case GLSLstd450Cos: {
auto val = cur_function->values[OP_WORD(4)];
auto ty = val->getType();//->getScalarType();
put_value(rid, builder->CreateIntrinsic(llvm::Intrinsic::cos, ty, val));
break;
}
// case GLSLstd450Atanh: {
// auto val = cur_function->values[OP_WORD(4)];
// auto ty = val->getType()->getScalarType();
// put_value(rid, builder->CreateIntrinsic(llvm::Intrinsic::))
// }
default: {
put_value(rid, builder->CreateFreeze(llvm::UndefValue::get(types[rty]->get_llvm_type())));
BOOST_LOG_TRIVIAL(warning) << "Unhandled GLSL extinst " << ext_inst;
}
}
break;
}
default: {
// ignore
}
}
break;
}
//endregion
//region 3.42.7 Constant-creation instructions
case Op::OpConstantTrue: {
constants[rid] = Constant(uint32_t(1));
constants[rid] = std::make_shared<Constant>(uint32_t(1));
break;
}
case Op::OpConstantFalse:
constants[rid] = Constant(uint32_t(0));
constants[rid] = std::make_shared<Constant>(uint32_t(0));
break;
case Op::OpConstant: {
auto &type = types[rty];
@@ -475,60 +643,394 @@ public:
bits = (uint64_t)OP_BYTES(2)[0]
| ((uint64_t)(OP_BYTES(2)[1]) << 32);
memcpy(&val, &bits, 8);
constants[rid] = val;
constants[rid] = std::make_shared<Constant>(val);
break;
case 32:
// float
memcpy(&fval, OP_BYTES(2), 4);
constants[rid] = fval;
constants[rid] = std::make_shared<Constant>(fval);
break;
case 16:
// half; we don't implement this yet
BOOST_LOG_TRIVIAL(warning) << "Half-precision floats incompletely implemented";
constants[rid] = fval;
constants[rid] = std::make_shared<Constant>(fval);
break;
}
} else if (itype != nullptr) {
uint64_t val = 0;
switch (itype->width) {
case 64:
constants[rid] = (uint64_t)OP_BYTES(2)[0]
| ((uint64_t)(OP_BYTES(2)[1]) << 32);
constants[rid] = std::make_shared<Constant>((uint64_t)OP_BYTES(2)[0]
| ((uint64_t)(OP_BYTES(2)[1]) << 32));
break;
case 32:
constants[rid] = OP_WORD(2);
constants[rid] = std::make_shared<Constant>(OP_WORD(2));
}
}
break;
}
//endregion
//region Variables
case Op::OpVariable: {
case Op::OpConstantComposite: {
auto &type = types[rty];
type->build_llvm_type(*ctx);
auto &typ_id = typeid(*type);
std::vector<llvm::Constant*> elements;
for (int i = 2; i < insn->num_operands; i++) {
elements.push_back(constants[OP_WORD(i)]->get_llvm_const(*ctx));
}
llvm::Constant *result = nullptr;
if (typ_id == typeid(SpirvStructType)) {
result = llvm::ConstantStruct::get((llvm::StructType*)type->get_llvm_type(),
elements);
} else if (typ_id == typeid(SpirvVectorType)) {
result = llvm::ConstantVector::get(
elements
);
} else if (typ_id == typeid(SpirvArrayType)) {
result = llvm::ConstantArray::get(
(llvm::ArrayType*)type->get_llvm_type(),
elements
);
}
constants[rid] = std::make_shared<ConstComposite>(result);
break;
}
//endregion
//region
//region 3.42.8 Memory instructions
case Op::OpVariable: {
if (cur_function == nullptr) {
int g_count = (int)globals.size();
Global global = {
.var_type = types[OP_WORD(0)],
.element_no = g_count,
};
globals[OP_WORD(1)] = global;
// TODO: handle initializer
} else {
put_value(rid, builder->CreateAlloca(types[rty]->get_llvm_type()->getPointerElementType(), nullptr, value_names[rid]));
}
break;
}
case Op::OpLoad: {
put_value(rid, builder->CreateLoad(types[rty]->get_llvm_type(), cur_function->values[OP_WORD(2)]));
break;
}
case Op::OpStore: {
builder->CreateStore(cur_function->values[OP_WORD(1)], cur_function->values[OP_WORD(0)]);
break;
}
case Op::OpAccessChain: {
std::vector<llvm::Value*> indices;
indices.push_back(builder->getInt32(0));
for (int i = 3; i < insn->num_operands; i++) {
// TODO: handle the case where the index is a value
auto id = OP_WORD(i);
indices.push_back(cur_function->values[id]);
}
auto ptr = cur_function->values[OP_WORD(2)];
put_value(rid, builder->CreateGEP(
ptr->getType()->getPointerElementType(),
cur_function->values[OP_WORD(2)],
indices,
value_names[rid]));
break;
}
//endregion
default:BOOST_LOG_TRIVIAL(info) << "Unhandled instruction " << format_insn(insn);
//region 3.42.9 Function instructions
case Op::OpFunction: {
finish_tgv();
auto ftype = types[OP_WORD(3)];
auto ftype_llvm = (llvm::FunctionType*)ftype->get_llvm_type();
cur_function = vivify_function(ftype_llvm, OP_WORD(1));
if (!cur_function->function->empty()) {
BOOST_LOG_TRIVIAL(error) << "Started assembling into " << cur_function->function->getName().str()
<< " but it already had data";
} else {
builder->ClearInsertionPoint();
cur_function->cur_arg = 1; // 0 is the global struct
}
break;
}
case Op::OpFunctionParameter: {
put_value(rid, cur_function->function->getArg(cur_function->cur_arg++));
break;
}
case Op::OpFunctionEnd: {
cur_function = nullptr;
break;
}
case Op::OpFunctionCall: {
// Ugh, this is horrible. I need to create a function type here
// because we might not have seen the target function yet
std::vector<llvm::Type*> arg_types;
std::vector<llvm::Value*> arg_values;
arg_types.push_back(global_type->getPointerTo());
arg_values.push_back(cur_function->function->getArg(0));
for (int i = 3; i < insn->num_operands; i++) {
llvm::Value* value = cur_function->values[OP_WORD(i)];
arg_types.push_back(value->getType());
arg_values.push_back(value);
}
auto ftype = llvm::FunctionType::get(
types[OP_WORD(0)]->get_llvm_type(),
arg_types,
false);
auto fn = vivify_function(ftype, OP_WORD(3));
put_value(rid, builder->CreateCall(fn->function, arg_values));
break;
}
//endregion
// region 3.42.12 Composite instructions
case Op::OpVectorShuffle: {
std::vector<int> mask(OP_BYTES(4), OP_BYTES(insn->num_operands));
auto value = builder->CreateShuffleVector(
cur_function->values[OP_WORD(2)],
cur_function->values[OP_WORD(3)],
mask,
value_names[rid]
);
put_value(rid, value);
break;
}
case Op::OpCompositeConstruct: {
llvm::Value* composite = llvm::UndefValue::get(types[rty]->get_llvm_type());
int dst_idx = 0;
bool dst_is_vector = composite->getType()->isVectorTy();
for (int opin = 2; opin < insn->num_operands; opin++) {
auto op = cur_function->values[OP_WORD(opin)];
if (dst_is_vector && op->getType()->isVectorTy()) {
// copy elementwise
auto sty = (llvm::VectorType*)op->getType();
for (unsigned int sidx = 0; sidx < sty->getElementCount().getFixedValue(); sidx++) {
auto el = builder->CreateExtractElement(op, sidx);
composite = builder->CreateInsertElement(composite, el, dst_idx++);
}
} else if (dst_is_vector) {
composite = builder->CreateInsertElement(composite, op, dst_idx++);
} else {
composite = builder->CreateInsertValue(composite, op, dst_idx);
dst_idx++;
}
}
put_value(rid, composite);
//
break;
}
case Op::OpCompositeExtract: {
auto value = cur_function->values[OP_WORD(2)];
for (int opno = 3; opno < insn->num_operands; opno++) {
auto op = OP_WORD(opno);
if (value->getType()->isVectorTy()) {
value = builder->CreateExtractElement(value, op);
} else {
value = builder->CreateExtractValue(value, op);
}
}
put_value(rid, value);
break;
}
//endregion
//region 3.42.13 Arithmetic instructions
case Op::OpFAdd: {
auto value = builder->CreateFAdd(cur_function->values[OP_WORD(2)],
cur_function->values[OP_WORD(3)]);
put_value(rid, value);
break;
}
case Op::OpFDiv: {
auto value =
builder->CreateFDiv(cur_function->values[OP_WORD(2)],
cur_function->values[OP_WORD(3)]);
put_value(rid, value);
break;
}
case Op::OpVectorTimesScalar: {
auto vty = (llvm::VectorType*)(types[rty]->get_llvm_type());
auto scalar = builder->CreateVectorSplat(vty->getElementCount(), cur_function->values[OP_WORD(3)]);
auto result = builder->CreateFMul(cur_function->values[OP_WORD(2)], scalar);
put_value(rid, result);
break;
}
//endregion
//region 3.42.17 Control Flow instructions
case Op::OpLabel: {
llvm::BasicBlock *new_bb = cur_function->basic_blocks[rid];
if (new_bb == nullptr) {
auto label = boost::format("label_%1%") % rid;
new_bb = cur_function->basic_blocks[rid] = llvm::BasicBlock::Create(*ctx, label.str(), cur_function->function);
}
builder->SetInsertPoint(new_bb);
if (cur_function->in_prologue) {
cur_function->gen_prologue(*this);
cur_function->in_prologue = false;
}
break;
}
case Op::OpReturn: {
builder->CreateRetVoid();
break;
}
//endregion
default:{
BOOST_LOG_TRIVIAL(info) << "Unhandled instruction " << format_insn(insn);
if (cur_function != nullptr && has_rid && has_rty) {
auto value = builder->CreateFreeze(llvm::UndefValue::get(types[rty]->get_llvm_type()));
put_value(rid, value);
}
}
}
if (cur_function != nullptr && has_rid && cur_function->values[rid] != nullptr) {
auto name_it = value_names.find(rid);
if (name_it != value_names.end()) {
cur_function->values[rid]->setName(name_it->second);
}
}
return SPV_SUCCESS;
}
#pragma clang diagnostic pop
std::shared_ptr<FunctionContext> vivify_function(llvm::FunctionType *ftype, uint32_t id) {
auto it = functions.find(id);
if (it == functions.end()) {
BOOST_LOG_TRIVIAL(debug) << "Created function %" << id;
assert(ftype != nullptr);
auto function = std::make_shared<FunctionContext>(*this, ftype, id);
functions.emplace(id, function);
return function;
} else {
BOOST_LOG_TRIVIAL(debug) << "Found pre-existing function %" << id;
return it->second;
}
}
private:
llvm::Value* get_value(uint32_t id, llvm::Type* type) {
auto value_it = cur_function->values.find(id);
if (value_it != cur_function->values.end()) {
return value_it->second;
} else {
auto value = builder->CreateFreeze(llvm::UndefValue::get(type));
cur_function->values[id] = value;
cur_function->speculative_values[id] = value;
return value;
}
}
void put_value(uint32_t id, llvm::Value* value) {
auto sv_it = cur_function->speculative_values.find(id);
if (sv_it != cur_function->speculative_values.end()) {
sv_it->second->replaceAllUsesWith(value);
cur_function->speculative_values.erase(sv_it);
}
cur_function->values[id] = value;
}
};
void InitLLVM() {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetDisassembler();
}
Compiler::Compiler(): impl(std::make_unique<CompilerImpl>()) {
}
bool Compiler::compile(std::vector<uint32_t> &spv_module) {
return impl->process_module(spv_module);
auto ret = impl->process_module(spv_module);
impl->module->print(llvm::outs(), nullptr, false, true);
BOOST_LOG_TRIVIAL(debug) << "Optimizing";
// start generating machine code
auto target_triple = llvm::sys::getProcessTriple();
std::string error;
auto target = llvm::TargetRegistry::lookupTarget(target_triple, error);
if (!target) {
BOOST_LOG_TRIVIAL(error) << "Failed to load target: " << error;
return false;
}
llvm::TargetOptions opt;
auto CPU = llvm::sys::getHostCPUName();
auto reloc_model = llvm::Optional<llvm::Reloc::Model>();
auto code_model = llvm::Optional<llvm::CodeModel::Model>();
auto target_machine = target->createTargetMachine(target_triple, CPU, "", opt, reloc_model, code_model, llvm::CodeGenOpt::Aggressive, true);
impl->module->setDataLayout(target_machine->createDataLayout());
impl->module->setTargetTriple(target_triple);
llvm::PassManagerBuilder pass_builder;
target_machine->adjustPassManager(pass_builder);
llvm::legacy::PassManager pass;
pass_builder.populateModulePassManager(pass);
pass.run(*impl->module);
impl->module->print(llvm::outs(), nullptr, false, true);
return ret;
}
Compiler::~Compiler() = default;
FunctionContext::FunctionContext(struct CompilerImpl &compiler, llvm::FunctionType *ftype, uint32_t id) {
bool is_ep = false;
std::string name;
auto linkage = llvm::Function::ExternalLinkage;
for (auto &ep: compiler.entry_points) {
if (ep.second->func_id == id) {
is_ep = true;
name = ep.second->name;
linkage = llvm::Function::ExternalLinkage;
break;
}
}
if (name.empty()) {
auto it = compiler.value_names.find(id);
if (it != compiler.value_names.end()) {
name = it->second;
}
}
for (auto &v: compiler.constants) {
values[v.first] = v.second->get_llvm_const(*compiler.ctx);
}
if (name.empty()) {
std::stringstream name_builder;
name_builder << "shaderfunc_" << id;
name = name_builder.str();
}
function = llvm::Function::Create(ftype, linkage, name, compiler.module.get());
function->getArg(0)->setName("globals");
in_prologue = true;
cur_arg = 1;
}
void FunctionContext::gen_prologue(struct CompilerImpl &compiler) {
// To be called after a basic block is created
auto &builder = *compiler.builder;
std::vector<llvm::Value*> indices;
auto itype = llvm::Type::getInt32Ty(*compiler.ctx);
indices.push_back(llvm::ConstantInt::get(itype, 0, false));
indices.push_back(nullptr);
for (auto &global: compiler.globals) {
indices[1] = llvm::ConstantInt::get(itype, global.second.element_no, false);
values[global.first] = builder.CreateGEP(
compiler.global_type,
function->getArg(0),
indices,
compiler.value_names[global.first]);
}
}
spv_result_t
impl_parse_header(void *user_data, spv_endianness_t endian, uint32_t magic, uint32_t version, uint32_t generator,
uint32_t id_bound, uint32_t reserved) {

View File

@@ -15,4 +15,6 @@ public:
bool compile(std::vector<uint32_t> &spv_module);
virtual ~Compiler();
};
};
void InitLLVM();

View File

@@ -13,7 +13,7 @@ std::shared_ptr<std::vector<uint32_t>> pre_compile_shader(const std::string &src
shaderc::Compiler compiler;
shaderc::CompileOptions options;
options.SetSourceLanguage(shaderc_source_language_glsl);
options.SetOptimizationLevel(shaderc_optimization_level_performance);
// options.SetOptimizationLevel(shaderc_optimization_level_performance);
options.SetTargetSpirv(shaderc_spirv_version_1_0);
options.SetTargetEnvironment(
shaderc_target_env_opengl_compat,

332
src/spv_insns.inc Normal file
View File

@@ -0,0 +1,332 @@
{ 0, "OpNop" },
{ 1, "OpUndef" },
{ 2, "OpSourceContinued" },
{ 3, "OpSource" },
{ 4, "OpSourceExtension" },
{ 5, "OpName" },
{ 6, "OpMemberName" },
{ 7, "OpString" },
{ 8, "OpLine" },
{ 10, "OpExtension" },
{ 11, "OpExtInstImport" },
{ 12, "OpExtInst" },
{ 14, "OpMemoryModel" },
{ 15, "OpEntryPoint" },
{ 16, "OpExecutionMode" },
{ 17, "OpCapability" },
{ 19, "OpTypeVoid" },
{ 20, "OpTypeBool" },
{ 21, "OpTypeInt" },
{ 22, "OpTypeFloat" },
{ 23, "OpTypeVector" },
{ 24, "OpTypeMatrix" },
{ 25, "OpTypeImage" },
{ 26, "OpTypeSampler" },
{ 27, "OpTypeSampledImage" },
{ 28, "OpTypeArray" },
{ 29, "OpTypeRuntimeArray" },
{ 30, "OpTypeStruct" },
{ 31, "OpTypeOpaque" },
{ 32, "OpTypePointer" },
{ 33, "OpTypeFunction" },
{ 34, "OpTypeEvent" },
{ 35, "OpTypeDeviceEvent" },
{ 36, "OpTypeReserveId" },
{ 37, "OpTypeQueue" },
{ 38, "OpTypePipe" },
{ 39, "OpTypeForwardPointer" },
{ 41, "OpConstantTrue" },
{ 42, "OpConstantFalse" },
{ 43, "OpConstant" },
{ 44, "OpConstantComposite" },
{ 45, "OpConstantSampler" },
{ 46, "OpConstantNull" },
{ 48, "OpSpecConstantTrue" },
{ 49, "OpSpecConstantFalse" },
{ 50, "OpSpecConstant" },
{ 51, "OpSpecConstantComposite" },
{ 52, "OpSpecConstantOp" },
{ 54, "OpFunction" },
{ 55, "OpFunctionParameter" },
{ 56, "OpFunctionEnd" },
{ 57, "OpFunctionCall" },
{ 59, "OpVariable" },
{ 60, "OpImageTexelPointer" },
{ 61, "OpLoad" },
{ 62, "OpStore" },
{ 63, "OpCopyMemory" },
{ 64, "OpCopyMemorySized" },
{ 65, "OpAccessChain" },
{ 66, "OpInBoundsAccessChain" },
{ 67, "OpPtrAccessChain" },
{ 68, "OpArrayLength" },
{ 69, "OpGenericPtrMemSemantics" },
{ 70, "OpInBoundsPtrAccessChain" },
{ 71, "OpDecorate" },
{ 72, "OpMemberDecorate" },
{ 73, "OpDecorationGroup" },
{ 74, "OpGroupDecorate" },
{ 75, "OpGroupMemberDecorate" },
{ 77, "OpVectorExtractDynamic" },
{ 78, "OpVectorInsertDynamic" },
{ 79, "OpVectorShuffle" },
{ 80, "OpCompositeConstruct" },
{ 81, "OpCompositeExtract" },
{ 82, "OpCompositeInsert" },
{ 83, "OpCopyObject" },
{ 84, "OpTranspose" },
{ 86, "OpSampledImage" },
{ 87, "OpImageSampleImplicitLod" },
{ 88, "OpImageSampleExplicitLod" },
{ 89, "OpImageSampleDrefImplicitLod" },
{ 90, "OpImageSampleDrefExplicitLod" },
{ 91, "OpImageSampleProjImplicitLod" },
{ 92, "OpImageSampleProjExplicitLod" },
{ 93, "OpImageSampleProjDrefImplicitLod" },
{ 94, "OpImageSampleProjDrefExplicitLod" },
{ 95, "OpImageFetch" },
{ 96, "OpImageGather" },
{ 97, "OpImageDrefGather" },
{ 98, "OpImageRead" },
{ 99, "OpImageWrite" },
{ 100, "OpImage" },
{ 101, "OpImageQueryFormat" },
{ 102, "OpImageQueryOrder" },
{ 103, "OpImageQuerySizeLod" },
{ 104, "OpImageQuerySize" },
{ 105, "OpImageQueryLod" },
{ 106, "OpImageQueryLevels" },
{ 107, "OpImageQuerySamples" },
{ 109, "OpConvertFToU" },
{ 110, "OpConvertFToS" },
{ 111, "OpConvertSToF" },
{ 112, "OpConvertUToF" },
{ 113, "OpUConvert" },
{ 114, "OpSConvert" },
{ 115, "OpFConvert" },
{ 116, "OpQuantizeToF16" },
{ 117, "OpConvertPtrToU" },
{ 118, "OpSatConvertSToU" },
{ 119, "OpSatConvertUToS" },
{ 120, "OpConvertUToPtr" },
{ 121, "OpPtrCastToGeneric" },
{ 122, "OpGenericCastToPtr" },
{ 123, "OpGenericCastToPtrExplicit" },
{ 124, "OpBitcast" },
{ 126, "OpSNegate" },
{ 127, "OpFNegate" },
{ 128, "OpIAdd" },
{ 129, "OpFAdd" },
{ 130, "OpISub" },
{ 131, "OpFSub" },
{ 132, "OpIMul" },
{ 133, "OpFMul" },
{ 134, "OpUDiv" },
{ 135, "OpSDiv" },
{ 136, "OpFDiv" },
{ 137, "OpUMod" },
{ 138, "OpSRem" },
{ 139, "OpSMod" },
{ 140, "OpFRem" },
{ 141, "OpFMod" },
{ 142, "OpVectorTimesScalar" },
{ 143, "OpMatrixTimesScalar" },
{ 144, "OpVectorTimesMatrix" },
{ 145, "OpMatrixTimesVector" },
{ 146, "OpMatrixTimesMatrix" },
{ 147, "OpOuterProduct" },
{ 148, "OpDot" },
{ 149, "OpIAddCarry" },
{ 150, "OpISubBorrow" },
{ 151, "OpUMulExtended" },
{ 152, "OpSMulExtended" },
{ 154, "OpAny" },
{ 155, "OpAll" },
{ 156, "OpIsNan" },
{ 157, "OpIsInf" },
{ 158, "OpIsFinite" },
{ 159, "OpIsNormal" },
{ 160, "OpSignBitSet" },
{ 161, "OpLessOrGreater" },
{ 162, "OpOrdered" },
{ 163, "OpUnordered" },
{ 164, "OpLogicalEqual" },
{ 165, "OpLogicalNotEqual" },
{ 166, "OpLogicalOr" },
{ 167, "OpLogicalAnd" },
{ 168, "OpLogicalNot" },
{ 169, "OpSelect" },
{ 170, "OpIEqual" },
{ 171, "OpINotEqual" },
{ 172, "OpUGreaterThan" },
{ 173, "OpSGreaterThan" },
{ 174, "OpUGreaterThanEqual" },
{ 175, "OpSGreaterThanEqual" },
{ 176, "OpULessThan" },
{ 177, "OpSLessThan" },
{ 178, "OpULessThanEqual" },
{ 179, "OpSLessThanEqual" },
{ 180, "OpFOrdEqual" },
{ 181, "OpFUnordEqual" },
{ 182, "OpFOrdNotEqual" },
{ 183, "OpFUnordNotEqual" },
{ 184, "OpFOrdLessThan" },
{ 185, "OpFUnordLessThan" },
{ 186, "OpFOrdGreaterThan" },
{ 187, "OpFUnordGreaterThan" },
{ 188, "OpFOrdLessThanEqual" },
{ 189, "OpFUnordLessThanEqual" },
{ 190, "OpFOrdGreaterThanEqual" },
{ 191, "OpFUnordGreaterThanEqual" },
{ 194, "OpShiftRightLogical" },
{ 195, "OpShiftRightArithmetic" },
{ 196, "OpShiftLeftLogical" },
{ 197, "OpBitwiseOr" },
{ 198, "OpBitwiseXor" },
{ 199, "OpBitwiseAnd" },
{ 200, "OpNot" },
{ 201, "OpBitFieldInsert" },
{ 202, "OpBitFieldSExtract" },
{ 203, "OpBitFieldUExtract" },
{ 204, "OpBitReverse" },
{ 205, "OpBitCount" },
{ 207, "OpDPdx" },
{ 208, "OpDPdy" },
{ 209, "OpFwidth" },
{ 210, "OpDPdxFine" },
{ 211, "OpDPdyFine" },
{ 212, "OpFwidthFine" },
{ 213, "OpDPdxCoarse" },
{ 214, "OpDPdyCoarse" },
{ 215, "OpFwidthCoarse" },
{ 218, "OpEmitVertex" },
{ 219, "OpEndPrimitive" },
{ 220, "OpEmitStreamVertex" },
{ 221, "OpEndStreamPrimitive" },
{ 224, "OpControlBarrier" },
{ 225, "OpMemoryBarrier" },
{ 227, "OpAtomicLoad" },
{ 228, "OpAtomicStore" },
{ 229, "OpAtomicExchange" },
{ 230, "OpAtomicCompareExchange" },
{ 231, "OpAtomicCompareExchangeWeak" },
{ 232, "OpAtomicIIncrement" },
{ 233, "OpAtomicIDecrement" },
{ 234, "OpAtomicIAdd" },
{ 235, "OpAtomicISub" },
{ 236, "OpAtomicSMin" },
{ 237, "OpAtomicUMin" },
{ 238, "OpAtomicSMax" },
{ 239, "OpAtomicUMax" },
{ 240, "OpAtomicAnd" },
{ 241, "OpAtomicOr" },
{ 242, "OpAtomicXor" },
{ 245, "OpPhi" },
{ 246, "OpLoopMerge" },
{ 247, "OpSelectionMerge" },
{ 248, "OpLabel" },
{ 249, "OpBranch" },
{ 250, "OpBranchConditional" },
{ 251, "OpSwitch" },
{ 252, "OpKill" },
{ 253, "OpReturn" },
{ 254, "OpReturnValue" },
{ 255, "OpUnreachable" },
{ 256, "OpLifetimeStart" },
{ 257, "OpLifetimeStop" },
{ 259, "OpGroupAsyncCopy" },
{ 260, "OpGroupWaitEvents" },
{ 261, "OpGroupAll" },
{ 262, "OpGroupAny" },
{ 263, "OpGroupBroadcast" },
{ 264, "OpGroupIAdd" },
{ 265, "OpGroupFAdd" },
{ 266, "OpGroupFMin" },
{ 267, "OpGroupUMin" },
{ 268, "OpGroupSMin" },
{ 269, "OpGroupFMax" },
{ 270, "OpGroupUMax" },
{ 271, "OpGroupSMax" },
{ 274, "OpReadPipe" },
{ 275, "OpWritePipe" },
{ 276, "OpReservedReadPipe" },
{ 277, "OpReservedWritePipe" },
{ 278, "OpReserveReadPipePackets" },
{ 279, "OpReserveWritePipePackets" },
{ 280, "OpCommitReadPipe" },
{ 281, "OpCommitWritePipe" },
{ 282, "OpIsValidReserveId" },
{ 283, "OpGetNumPipePackets" },
{ 284, "OpGetMaxPipePackets" },
{ 285, "OpGroupReserveReadPipePackets" },
{ 286, "OpGroupReserveWritePipePackets" },
{ 287, "OpGroupCommitReadPipe" },
{ 288, "OpGroupCommitWritePipe" },
{ 291, "OpEnqueueMarker" },
{ 292, "OpEnqueueKernel" },
{ 293, "OpGetKernelNDrangeSubGroupCount" },
{ 294, "OpGetKernelNDrangeMaxSubGroupSize" },
{ 295, "OpGetKernelWorkGroupSize" },
{ 296, "OpGetKernelPreferredWorkGroupSizeMultiple" },
{ 297, "OpRetainEvent" },
{ 298, "OpReleaseEvent" },
{ 299, "OpCreateUserEvent" },
{ 300, "OpIsValidEvent" },
{ 301, "OpSetUserEventStatus" },
{ 302, "OpCaptureEventProfilingInfo" },
{ 303, "OpGetDefaultQueue" },
{ 304, "OpBuildNDRange" },
{ 305, "OpImageSparseSampleImplicitLod" },
{ 306, "OpImageSparseSampleExplicitLod" },
{ 307, "OpImageSparseSampleDrefImplicitLod" },
{ 308, "OpImageSparseSampleDrefExplicitLod" },
{ 309, "OpImageSparseSampleProjImplicitLod" },
{ 310, "OpImageSparseSampleProjExplicitLod" },
{ 311, "OpImageSparseSampleProjDrefImplicitLod" },
{ 312, "OpImageSparseSampleProjDrefExplicitLod" },
{ 313, "OpImageSparseFetch" },
{ 314, "OpImageSparseGather" },
{ 315, "OpImageSparseDrefGather" },
{ 316, "OpImageSparseTexelsResident" },
{ 317, "OpNoLine" },
{ 318, "OpAtomicFlagTestAndSet" },
{ 319, "OpAtomicFlagClear" },
{ 320, "OpImageSparseRead" },
{ 321, "OpSizeOf" },
{ 322, "OpTypePipeStorage" },
{ 323, "OpConstantPipeStorage" },
{ 324, "OpCreatePipeFromPipeStorage" },
{ 325, "OpGetKernelLocalSizeForSubgroupCount" },
{ 326, "OpGetKernelMaxNumSubgroups" },
{ 327, "OpTypeNamedBarrier" },
{ 328, "OpNamedBarrierInitialize" },
{ 329, "OpMemoryNamedBarrier" },
{ 330, "OpModuleProcessed" },
{ 331, "OpExecutionModeId" },
{ 332, "OpDecorateId" },
{ 4421, "OpSubgroupBallotKHR" },
{ 4422, "OpSubgroupFirstInvocationKHR" },
{ 4428, "OpSubgroupAllKHR" },
{ 4429, "OpSubgroupAnyKHR" },
{ 4430, "OpSubgroupAllEqualKHR" },
{ 4432, "OpSubgroupReadInvocationKHR" },
{ 5000, "OpGroupIAddNonUniformAMD" },
{ 5001, "OpGroupFAddNonUniformAMD" },
{ 5002, "OpGroupFMinNonUniformAMD" },
{ 5003, "OpGroupUMinNonUniformAMD" },
{ 5004, "OpGroupSMinNonUniformAMD" },
{ 5005, "OpGroupFMaxNonUniformAMD" },
{ 5006, "OpGroupUMaxNonUniformAMD" },
{ 5007, "OpGroupSMaxNonUniformAMD" },
{ 5011, "OpFragmentMaskFetchAMD" },
{ 5012, "OpFragmentFetchAMD" },
{ 5571, "OpSubgroupShuffleINTEL" },
{ 5572, "OpSubgroupShuffleDownINTEL" },
{ 5573, "OpSubgroupShuffleUpINTEL" },
{ 5574, "OpSubgroupShuffleXorINTEL" },
{ 5575, "OpSubgroupBlockReadINTEL" },
{ 5576, "OpSubgroupBlockWriteINTEL" },
{ 5577, "OpSubgroupImageBlockReadINTEL" },
{ 5578, "OpSubgroupImageBlockWriteINTEL" },
{ 5632, "OpDecorateStringGOOGLE" },
{ 5633, "OpMemberDecorateStringGOOGLE" },

22
src/spv_meta.cpp Normal file
View File

@@ -0,0 +1,22 @@
//
// Created by thequux on 5/25/22.
//
#include "spv_meta.hpp"
static struct {
uint16_t op_id;
const char* name;
} op_names_in[] = {
#include "spv_insns.inc"
{0, nullptr}
};
std::map<uint32_t, std::string> insn_names;
void build_spv_meta() {
for (int i = 0; op_names_in[i].name != nullptr; i++) {
auto &op = op_names_in[i];
insn_names.emplace(op.op_id, op.name);
}
}

11
src/spv_meta.hpp Normal file
View File

@@ -0,0 +1,11 @@
//
// Created by thequux on 5/25/22.
//
#pragma once
#include <map>
#include <string>
extern std::map<uint32_t, std::string> insn_names;
void build_spv_meta();