You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1225 lines
43 KiB
1225 lines
43 KiB
/*
|
|
* Copyright 2010-2012, The Android Open Source Project
|
|
*
|
|
* 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 "slang_backend.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <iostream>
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Attr.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclGroup.h"
|
|
#include "clang/AST/RecordLayout.h"
|
|
|
|
#include "clang/Basic/Diagnostic.h"
|
|
#include "clang/Basic/TargetInfo.h"
|
|
#include "clang/Basic/TargetOptions.h"
|
|
|
|
#include "clang/CodeGen/ModuleBuilder.h"
|
|
|
|
#include "clang/Frontend/CodeGenOptions.h"
|
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
|
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
|
|
#include "llvm/Bitcode/ReaderWriter.h"
|
|
|
|
#include "llvm/CodeGen/RegAllocRegistry.h"
|
|
#include "llvm/CodeGen/SchedulerRegistry.h"
|
|
|
|
#include "llvm/IR/Constant.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DataLayout.h"
|
|
#include "llvm/IR/DebugLoc.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/IRPrintingPasses.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/Module.h"
|
|
|
|
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
|
|
|
#include "llvm/Target/TargetMachine.h"
|
|
#include "llvm/Target/TargetOptions.h"
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
|
|
#include "llvm/MC/SubtargetFeature.h"
|
|
|
|
#include "slang_assert.h"
|
|
#include "slang.h"
|
|
#include "slang_bitcode_gen.h"
|
|
#include "slang_rs_context.h"
|
|
#include "slang_rs_export_foreach.h"
|
|
#include "slang_rs_export_func.h"
|
|
#include "slang_rs_export_reduce.h"
|
|
#include "slang_rs_export_type.h"
|
|
#include "slang_rs_export_var.h"
|
|
#include "slang_rs_metadata.h"
|
|
|
|
#include "rs_cc_options.h"
|
|
|
|
#include "StripUnkAttr/strip_unknown_attributes_pass.h"
|
|
|
|
namespace {
|
|
class VersionInfoPass : public llvm::ModulePass {
|
|
const clang::CodeGenOptions &mCodeGenOpts;
|
|
|
|
const char *getSlangLLVMVersion() const {
|
|
if (mCodeGenOpts.getDebugInfo() != clang::codegenoptions::NoDebugInfo)
|
|
return LLVM_VERSION_STRING;
|
|
return nullptr;
|
|
}
|
|
|
|
public:
|
|
static char ID;
|
|
VersionInfoPass(const clang::CodeGenOptions &codegenOpts)
|
|
: ModulePass(ID), mCodeGenOpts(codegenOpts) {}
|
|
virtual bool runOnModule(llvm::Module &M) override {
|
|
const char *versionString = getSlangLLVMVersion();
|
|
if (!versionString)
|
|
return false;
|
|
auto &ctx = M.getContext();
|
|
auto md = M.getOrInsertNamedMetadata("slang.llvm.version");
|
|
auto ver = llvm::MDString::get(ctx, versionString);
|
|
md->addOperand(
|
|
llvm::MDNode::get(ctx, llvm::ArrayRef<llvm::Metadata *>(ver)));
|
|
return true;
|
|
}
|
|
};
|
|
|
|
char VersionInfoPass::ID = 0;
|
|
|
|
llvm::ModulePass *createVersionInfoPass(const clang::CodeGenOptions &cgo) {
|
|
return new VersionInfoPass(cgo);
|
|
}
|
|
}
|
|
|
|
namespace slang {
|
|
|
|
void Backend::CreateFunctionPasses() {
|
|
if (!mPerFunctionPasses) {
|
|
mPerFunctionPasses = new llvm::legacy::FunctionPassManager(mpModule);
|
|
|
|
llvm::PassManagerBuilder PMBuilder;
|
|
PMBuilder.OptLevel = mCodeGenOpts.OptimizationLevel;
|
|
PMBuilder.populateFunctionPassManager(*mPerFunctionPasses);
|
|
}
|
|
}
|
|
|
|
void Backend::CreateModulePasses() {
|
|
if (!mPerModulePasses) {
|
|
mPerModulePasses = new llvm::legacy::PassManager();
|
|
|
|
llvm::PassManagerBuilder PMBuilder;
|
|
PMBuilder.OptLevel = mCodeGenOpts.OptimizationLevel;
|
|
PMBuilder.SizeLevel = mCodeGenOpts.OptimizeSize;
|
|
PMBuilder.DisableUnitAtATime = 0; // TODO Pirama confirm if this is right
|
|
|
|
if (mCodeGenOpts.UnrollLoops) {
|
|
PMBuilder.DisableUnrollLoops = 0;
|
|
} else {
|
|
PMBuilder.DisableUnrollLoops = 1;
|
|
}
|
|
|
|
PMBuilder.populateModulePassManager(*mPerModulePasses);
|
|
// Add a pass to strip off unknown/unsupported attributes.
|
|
mPerModulePasses->add(createStripUnknownAttributesPass());
|
|
if (!mContext->isCompatLib()) {
|
|
// The version info pass is used to ensure that debugging
|
|
// is matched between slang and bcc.
|
|
mPerModulePasses->add(createVersionInfoPass(mCodeGenOpts));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Backend::CreateCodeGenPasses() {
|
|
if ((mOT != Slang::OT_Assembly) && (mOT != Slang::OT_Object))
|
|
return true;
|
|
|
|
// Now we add passes for code emitting
|
|
if (mCodeGenPasses) {
|
|
return true;
|
|
} else {
|
|
mCodeGenPasses = new llvm::legacy::FunctionPassManager(mpModule);
|
|
}
|
|
|
|
// Create the TargetMachine for generating code.
|
|
std::string Triple = mpModule->getTargetTriple();
|
|
|
|
std::string Error;
|
|
const llvm::Target* TargetInfo =
|
|
llvm::TargetRegistry::lookupTarget(Triple, Error);
|
|
if (TargetInfo == nullptr) {
|
|
mDiagEngine.Report(clang::diag::err_fe_unable_to_create_target) << Error;
|
|
return false;
|
|
}
|
|
|
|
// Target Machine Options
|
|
llvm::TargetOptions Options;
|
|
|
|
// Use soft-float ABI for ARM (which is the target used by Slang during code
|
|
// generation). Codegen still uses hardware FPU by default. To use software
|
|
// floating point, add 'soft-float' feature to FeaturesStr below.
|
|
Options.FloatABIType = llvm::FloatABI::Soft;
|
|
|
|
// BCC needs all unknown symbols resolved at compilation time. So we don't
|
|
// need any relocation model.
|
|
llvm::Reloc::Model RM = llvm::Reloc::Static;
|
|
|
|
// This is set for the linker (specify how large of the virtual addresses we
|
|
// can access for all unknown symbols.)
|
|
llvm::CodeModel::Model CM;
|
|
if (mpModule->getDataLayout().getPointerSize() == 4) {
|
|
CM = llvm::CodeModel::Small;
|
|
} else {
|
|
// The target may have pointer size greater than 32 (e.g. x86_64
|
|
// architecture) may need large data address model
|
|
CM = llvm::CodeModel::Medium;
|
|
}
|
|
|
|
// Setup feature string
|
|
std::string FeaturesStr;
|
|
if (mTargetOpts.CPU.size() || mTargetOpts.Features.size()) {
|
|
llvm::SubtargetFeatures Features;
|
|
|
|
for (std::vector<std::string>::const_iterator
|
|
I = mTargetOpts.Features.begin(), E = mTargetOpts.Features.end();
|
|
I != E;
|
|
I++)
|
|
Features.AddFeature(*I);
|
|
|
|
FeaturesStr = Features.getString();
|
|
}
|
|
|
|
llvm::TargetMachine *TM =
|
|
TargetInfo->createTargetMachine(Triple, mTargetOpts.CPU, FeaturesStr,
|
|
Options, RM, CM);
|
|
|
|
// Register allocation policy:
|
|
// createFastRegisterAllocator: fast but bad quality
|
|
// createGreedyRegisterAllocator: not so fast but good quality
|
|
llvm::RegisterRegAlloc::setDefault((mCodeGenOpts.OptimizationLevel == 0) ?
|
|
llvm::createFastRegisterAllocator :
|
|
llvm::createGreedyRegisterAllocator);
|
|
|
|
llvm::CodeGenOpt::Level OptLevel = llvm::CodeGenOpt::Default;
|
|
if (mCodeGenOpts.OptimizationLevel == 0) {
|
|
OptLevel = llvm::CodeGenOpt::None;
|
|
} else if (mCodeGenOpts.OptimizationLevel == 3) {
|
|
OptLevel = llvm::CodeGenOpt::Aggressive;
|
|
}
|
|
|
|
llvm::TargetMachine::CodeGenFileType CGFT =
|
|
llvm::TargetMachine::CGFT_AssemblyFile;
|
|
if (mOT == Slang::OT_Object) {
|
|
CGFT = llvm::TargetMachine::CGFT_ObjectFile;
|
|
}
|
|
if (TM->addPassesToEmitFile(*mCodeGenPasses, mBufferOutStream,
|
|
CGFT, OptLevel)) {
|
|
mDiagEngine.Report(clang::diag::err_fe_unable_to_interface_with_target);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Backend::Backend(RSContext *Context, clang::DiagnosticsEngine *DiagEngine,
|
|
const RSCCOptions &Opts,
|
|
const clang::HeaderSearchOptions &HeaderSearchOpts,
|
|
const clang::PreprocessorOptions &PreprocessorOpts,
|
|
const clang::CodeGenOptions &CodeGenOpts,
|
|
const clang::TargetOptions &TargetOpts, PragmaList *Pragmas,
|
|
llvm::raw_ostream *OS, Slang::OutputType OT,
|
|
clang::SourceManager &SourceMgr, bool AllowRSPrefix,
|
|
bool IsFilterscript)
|
|
: ASTConsumer(), mTargetOpts(TargetOpts), mpModule(nullptr), mpOS(OS),
|
|
mOT(OT), mGen(nullptr), mPerFunctionPasses(nullptr),
|
|
mPerModulePasses(nullptr), mCodeGenPasses(nullptr),
|
|
mBufferOutStream(*mpOS), mContext(Context),
|
|
mSourceMgr(SourceMgr), mASTPrint(Opts.mASTPrint), mAllowRSPrefix(AllowRSPrefix),
|
|
mIsFilterscript(IsFilterscript), mExportVarMetadata(nullptr),
|
|
mExportFuncMetadata(nullptr), mExportForEachNameMetadata(nullptr),
|
|
mExportForEachSignatureMetadata(nullptr),
|
|
mExportReduceMetadata(nullptr),
|
|
mExportTypeMetadata(nullptr), mRSObjectSlotsMetadata(nullptr),
|
|
mRefCount(mContext->getASTContext()),
|
|
mASTChecker(Context, Context->getTargetAPI(), IsFilterscript),
|
|
mForEachHandler(Context),
|
|
mLLVMContext(slang::getGlobalLLVMContext()), mDiagEngine(*DiagEngine),
|
|
mCodeGenOpts(CodeGenOpts), mPragmas(Pragmas) {
|
|
mGen = CreateLLVMCodeGen(mDiagEngine, "", HeaderSearchOpts, PreprocessorOpts,
|
|
mCodeGenOpts, mLLVMContext);
|
|
}
|
|
|
|
void Backend::Initialize(clang::ASTContext &Ctx) {
|
|
mGen->Initialize(Ctx);
|
|
|
|
mpModule = mGen->GetModule();
|
|
}
|
|
|
|
void Backend::HandleTranslationUnit(clang::ASTContext &Ctx) {
|
|
HandleTranslationUnitPre(Ctx);
|
|
|
|
if (mASTPrint)
|
|
Ctx.getTranslationUnitDecl()->dump();
|
|
|
|
mGen->HandleTranslationUnit(Ctx);
|
|
|
|
// Here, we complete a translation unit (whole translation unit is now in LLVM
|
|
// IR). Now, interact with LLVM backend to generate actual machine code (asm
|
|
// or machine code, whatever.)
|
|
|
|
// Silently ignore if we weren't initialized for some reason.
|
|
if (!mpModule)
|
|
return;
|
|
|
|
llvm::Module *M = mGen->ReleaseModule();
|
|
if (!M) {
|
|
// The module has been released by IR gen on failures, do not double free.
|
|
mpModule = nullptr;
|
|
return;
|
|
}
|
|
|
|
slangAssert(mpModule == M &&
|
|
"Unexpected module change during LLVM IR generation");
|
|
|
|
// Insert #pragma information into metadata section of module
|
|
if (!mPragmas->empty()) {
|
|
llvm::NamedMDNode *PragmaMetadata =
|
|
mpModule->getOrInsertNamedMetadata(Slang::PragmaMetadataName);
|
|
for (PragmaList::const_iterator I = mPragmas->begin(), E = mPragmas->end();
|
|
I != E;
|
|
I++) {
|
|
llvm::SmallVector<llvm::Metadata*, 2> Pragma;
|
|
// Name goes first
|
|
Pragma.push_back(llvm::MDString::get(mLLVMContext, I->first));
|
|
// And then value
|
|
Pragma.push_back(llvm::MDString::get(mLLVMContext, I->second));
|
|
|
|
// Create MDNode and insert into PragmaMetadata
|
|
PragmaMetadata->addOperand(
|
|
llvm::MDNode::get(mLLVMContext, Pragma));
|
|
}
|
|
}
|
|
|
|
HandleTranslationUnitPost(mpModule);
|
|
|
|
// Create passes for optimization and code emission
|
|
|
|
// Create and run per-function passes
|
|
CreateFunctionPasses();
|
|
if (mPerFunctionPasses) {
|
|
mPerFunctionPasses->doInitialization();
|
|
|
|
for (llvm::Module::iterator I = mpModule->begin(), E = mpModule->end();
|
|
I != E;
|
|
I++)
|
|
if (!I->isDeclaration())
|
|
mPerFunctionPasses->run(*I);
|
|
|
|
mPerFunctionPasses->doFinalization();
|
|
}
|
|
|
|
// Create and run module passes
|
|
CreateModulePasses();
|
|
if (mPerModulePasses)
|
|
mPerModulePasses->run(*mpModule);
|
|
|
|
switch (mOT) {
|
|
case Slang::OT_Assembly:
|
|
case Slang::OT_Object: {
|
|
if (!CreateCodeGenPasses())
|
|
return;
|
|
|
|
mCodeGenPasses->doInitialization();
|
|
|
|
for (llvm::Module::iterator I = mpModule->begin(), E = mpModule->end();
|
|
I != E;
|
|
I++)
|
|
if (!I->isDeclaration())
|
|
mCodeGenPasses->run(*I);
|
|
|
|
mCodeGenPasses->doFinalization();
|
|
break;
|
|
}
|
|
case Slang::OT_LLVMAssembly: {
|
|
llvm::legacy::PassManager *LLEmitPM = new llvm::legacy::PassManager();
|
|
LLEmitPM->add(llvm::createPrintModulePass(mBufferOutStream));
|
|
LLEmitPM->run(*mpModule);
|
|
break;
|
|
}
|
|
case Slang::OT_Bitcode: {
|
|
writeBitcode(mBufferOutStream, *mpModule, getTargetAPI(),
|
|
mCodeGenOpts.OptimizationLevel, mCodeGenOpts.getDebugInfo());
|
|
break;
|
|
}
|
|
case Slang::OT_Nothing: {
|
|
return;
|
|
}
|
|
default: {
|
|
slangAssert(false && "Unknown output type");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Insert explicit padding fields into struct to follow the current layout.
|
|
//
|
|
// A similar algorithm is present in PadHelperFunctionStruct().
|
|
void Backend::PadStruct(clang::RecordDecl* RD) {
|
|
// Example of padding:
|
|
//
|
|
// // ORIGINAL CODE // TRANSFORMED CODE
|
|
// struct foo { struct foo {
|
|
// int a; int a;
|
|
// // 4 bytes of padding char <RS_PADDING_FIELD_NAME>[4];
|
|
// long b; long b;
|
|
// int c; int c;
|
|
// // 4 bytes of (tail) padding char <RS_PADDING_FIELD_NAME>[4];
|
|
// }; };
|
|
|
|
// We collect all of RD's fields in a vector FieldsInfo. We
|
|
// represent tail padding as an entry in the FieldsInfo vector with a
|
|
// null FieldDecl.
|
|
typedef std::pair<size_t, clang::FieldDecl*> FieldInfoType; // (pre-field padding bytes, field)
|
|
std::vector<FieldInfoType> FieldsInfo;
|
|
|
|
// RenderScript is C99-based, so we only expect to see fields. We
|
|
// could iterate over fields, but instead let's iterate over
|
|
// everything, to verify that there are only fields.
|
|
for (clang::Decl* D : RD->decls()) {
|
|
clang::FieldDecl* FD = clang::dyn_cast<clang::FieldDecl>(D);
|
|
slangAssert(FD && "found a non field declaration within a struct");
|
|
FieldsInfo.push_back(std::make_pair(size_t(0), FD));
|
|
}
|
|
|
|
clang::ASTContext& ASTC = mContext->getASTContext();
|
|
|
|
// ASTContext caches record layout. We may transform the record in a way
|
|
// that would render this cached information incorrect. clang does
|
|
// not provide any way to invalidate this cached information. We
|
|
// take the following approach:
|
|
//
|
|
// 1. ASSUME that record layout has not yet been computed for RD.
|
|
//
|
|
// 2. Create a temporary clone of RD, and compute its layout.
|
|
// ASSUME that we know how to clone RD in a way that copies all the
|
|
// properties that are relevant to its layout.
|
|
//
|
|
// 3. Use the layout information from the temporary clone to
|
|
// transform RD.
|
|
//
|
|
// NOTE: ASTContext also caches TypeInfo (see
|
|
// ASTContext::getTypeInfo()). ASSUME that inserting padding
|
|
// fields doesn't change the type in any way that affects
|
|
// TypeInfo.
|
|
//
|
|
// NOTE: A RecordType knows its associated RecordDecl -- so even
|
|
// while we're manipulating RD, the associated RecordType
|
|
// still recognizes RD as its RecordDecl. ASSUME that we
|
|
// don't do anything during our manipulation that would cause
|
|
// the RecordType to be followed to RD while RD is in a
|
|
// partially transformed state.
|
|
|
|
// The assumptions above may be brittle, and if they are incorrect,
|
|
// we may get mysterious failures.
|
|
|
|
// create a temporary clone
|
|
clang::RecordDecl* RDForLayout =
|
|
clang::RecordDecl::Create(ASTC, clang::TTK_Struct, RD->getDeclContext(),
|
|
clang::SourceLocation(), clang::SourceLocation(),
|
|
nullptr /* IdentifierInfo */);
|
|
RDForLayout->startDefinition();
|
|
RDForLayout->setTypeForDecl(RD->getTypeForDecl());
|
|
if (RD->hasAttrs())
|
|
RDForLayout->setAttrs(RD->getAttrs());
|
|
RDForLayout->completeDefinition();
|
|
|
|
// move all fields from RD to RDForLayout
|
|
for (const auto &info : FieldsInfo) {
|
|
RD->removeDecl(info.second);
|
|
info.second->setLexicalDeclContext(RDForLayout);
|
|
RDForLayout->addDecl(info.second);
|
|
}
|
|
|
|
const clang::ASTRecordLayout& RL = ASTC.getASTRecordLayout(RDForLayout);
|
|
|
|
// An exportable type cannot contain a bitfield. However, it's
|
|
// possible that this current type might have a bitfield and yet
|
|
// share a common initial sequence with an exportable type, so even
|
|
// if the current type has a bitfield, the current type still
|
|
// needs to have explicit padding inserted (in case the two types
|
|
// under discussion are members of a union). We don't need to
|
|
// insert any padding after the bitfield, however, because that
|
|
// would be beyond the common initial sequence.
|
|
bool foundBitField = false;
|
|
|
|
// Is there any padding in this struct?
|
|
bool foundPadding = false;
|
|
|
|
unsigned fieldNo = 0;
|
|
uint64_t fieldPrePaddingOffset = 0; // byte offset of pre-field padding within struct
|
|
for (auto &info : FieldsInfo) {
|
|
const clang::FieldDecl* FD = info.second;
|
|
|
|
if ((foundBitField = FD->isBitField()))
|
|
break;
|
|
|
|
const uint64_t fieldOffset = RL.getFieldOffset(fieldNo) >> 3;
|
|
const size_t prePadding = fieldOffset - fieldPrePaddingOffset;
|
|
foundPadding |= (prePadding != 0);
|
|
info.first = prePadding;
|
|
|
|
// get ready for the next field
|
|
//
|
|
// assumes that getTypeSize() is the storage size of the Type -- for example,
|
|
// that it includes a struct's tail padding (if any)
|
|
//
|
|
fieldPrePaddingOffset = fieldOffset + (ASTC.getTypeSize(FD->getType()) >> 3);
|
|
++fieldNo;
|
|
}
|
|
|
|
if (!foundBitField) {
|
|
// In order to ensure that the front end (including reflected
|
|
// code) and back end agree on struct size (not just field
|
|
// offsets) we may need to add explicit tail padding, just as we'e
|
|
// added explicit padding between fields.
|
|
slangAssert(RL.getSize().getQuantity() >= fieldPrePaddingOffset);
|
|
if (const size_t tailPadding = RL.getSize().getQuantity() - fieldPrePaddingOffset) {
|
|
foundPadding = true;
|
|
FieldsInfo.push_back(std::make_pair(tailPadding, nullptr));
|
|
}
|
|
}
|
|
|
|
if (false /* change to "true" for extra debugging output */) {
|
|
if (foundPadding) {
|
|
std::cout << "PadStruct(" << RD->getNameAsString() << "):" << std::endl;
|
|
for (const auto &info : FieldsInfo)
|
|
std::cout << " " << info.first << ", " << (info.second ? info.second->getNameAsString() : "<tail>") << std::endl;
|
|
}
|
|
}
|
|
|
|
if (foundPadding && Slang::IsLocInRSHeaderFile(RD->getLocation(), mSourceMgr)) {
|
|
mContext->ReportError(RD->getLocation(), "system structure contains padding: '%0'")
|
|
<< RD->getName();
|
|
}
|
|
|
|
// now move fields from RDForLayout to RD, and add any necessary
|
|
// padding fields
|
|
const clang::QualType byteType = ASTC.getIntTypeForBitwidth(8, false /* not signed */);
|
|
clang::IdentifierInfo* const paddingIdentifierInfo = &ASTC.Idents.get(RS_PADDING_FIELD_NAME);
|
|
for (const auto &info : FieldsInfo) {
|
|
if (info.first != 0) {
|
|
// Create a padding field: "char <RS_PADDING_FIELD_NAME>[<info.first>];"
|
|
|
|
// TODO: Do we need to do anything else to keep this field from being shown in debugger?
|
|
// There's no source location, and the field is marked as implicit.
|
|
const clang::QualType paddingType =
|
|
ASTC.getConstantArrayType(byteType,
|
|
llvm::APInt(sizeof(info.first) << 3, info.first),
|
|
clang::ArrayType::Normal, 0 /* IndexTypeQuals */);
|
|
clang::FieldDecl* const FD =
|
|
clang::FieldDecl::Create(ASTC, RD, clang::SourceLocation(), clang::SourceLocation(),
|
|
paddingIdentifierInfo,
|
|
paddingType,
|
|
nullptr, // TypeSourceInfo*
|
|
nullptr, // BW (bitwidth)
|
|
false, // Mutable = false
|
|
clang::ICIS_NoInit);
|
|
FD->setImplicit(true);
|
|
RD->addDecl(FD);
|
|
}
|
|
if (info.second != nullptr) {
|
|
RDForLayout->removeDecl(info.second);
|
|
info.second->setLexicalDeclContext(RD);
|
|
RD->addDecl(info.second);
|
|
}
|
|
}
|
|
|
|
// There does not appear to be any safe way to delete a RecordDecl
|
|
// -- for example, there is no RecordDecl destructor to invalidate
|
|
// cached record layout, and if we were to get unlucky, some future
|
|
// RecordDecl could be allocated in the same place as a deleted
|
|
// RDForLayout and "inherit" the cached record layout from
|
|
// RDForLayout.
|
|
}
|
|
|
|
void Backend::HandleTagDeclDefinition(clang::TagDecl *D) {
|
|
// we want to insert explicit padding fields into structs per http://b/29154200 and http://b/28070272
|
|
switch (D->getTagKind()) {
|
|
case clang::TTK_Struct:
|
|
PadStruct(llvm::cast<clang::RecordDecl>(D));
|
|
break;
|
|
|
|
case clang::TTK_Union:
|
|
// cannot be part of an exported type
|
|
break;
|
|
|
|
case clang::TTK_Enum:
|
|
// a scalar
|
|
break;
|
|
|
|
case clang::TTK_Class:
|
|
case clang::TTK_Interface:
|
|
default:
|
|
slangAssert(false && "Unexpected TagTypeKind");
|
|
break;
|
|
}
|
|
mGen->HandleTagDeclDefinition(D);
|
|
}
|
|
|
|
void Backend::CompleteTentativeDefinition(clang::VarDecl *D) {
|
|
mGen->CompleteTentativeDefinition(D);
|
|
}
|
|
|
|
Backend::~Backend() {
|
|
delete mpModule;
|
|
delete mGen;
|
|
delete mPerFunctionPasses;
|
|
delete mPerModulePasses;
|
|
delete mCodeGenPasses;
|
|
}
|
|
|
|
// 1) Add zero initialization of local RS object types
|
|
void Backend::AnnotateFunction(clang::FunctionDecl *FD) {
|
|
if (FD &&
|
|
FD->hasBody() &&
|
|
!FD->isImplicit() &&
|
|
!Slang::IsLocInRSHeaderFile(FD->getLocation(), mSourceMgr)) {
|
|
mRefCount.Init();
|
|
mRefCount.SetDeclContext(FD);
|
|
mRefCount.HandleParamsAndLocals(FD);
|
|
}
|
|
}
|
|
|
|
bool Backend::HandleTopLevelDecl(clang::DeclGroupRef D) {
|
|
// Find and remember the types for rs_allocation and rs_script_call_t so
|
|
// they can be used later for translating rsForEach() calls.
|
|
for (clang::DeclGroupRef::iterator I = D.begin(), E = D.end();
|
|
(mContext->getAllocationType().isNull() ||
|
|
mContext->getScriptCallType().isNull()) &&
|
|
I != E; I++) {
|
|
if (clang::TypeDecl* TD = llvm::dyn_cast<clang::TypeDecl>(*I)) {
|
|
clang::StringRef TypeName = TD->getName();
|
|
if (TypeName.equals("rs_allocation")) {
|
|
mContext->setAllocationType(TD);
|
|
} else if (TypeName.equals("rs_script_call_t")) {
|
|
mContext->setScriptCallType(TD);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Disallow user-defined functions with prefix "rs"
|
|
if (!mAllowRSPrefix) {
|
|
// Iterate all function declarations in the program.
|
|
for (clang::DeclGroupRef::iterator I = D.begin(), E = D.end();
|
|
I != E; I++) {
|
|
clang::FunctionDecl *FD = llvm::dyn_cast<clang::FunctionDecl>(*I);
|
|
if (FD == nullptr)
|
|
continue;
|
|
if (!FD->getName().startswith("rs")) // Check prefix
|
|
continue;
|
|
if (!Slang::IsLocInRSHeaderFile(FD->getLocation(), mSourceMgr))
|
|
mContext->ReportError(FD->getLocation(),
|
|
"invalid function name prefix, "
|
|
"\"rs\" is reserved: '%0'")
|
|
<< FD->getName();
|
|
}
|
|
}
|
|
|
|
for (clang::DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; I++) {
|
|
clang::FunctionDecl *FD = llvm::dyn_cast<clang::FunctionDecl>(*I);
|
|
if (FD) {
|
|
// Handle forward reference from pragma (see
|
|
// RSReducePragmaHandler::HandlePragma for backward reference).
|
|
mContext->markUsedByReducePragma(FD, RSContext::CheckNameYes);
|
|
if (FD->isGlobal()) {
|
|
// Check that we don't have any array parameters being misinterpreted as
|
|
// kernel pointers due to the C type system's array to pointer decay.
|
|
size_t numParams = FD->getNumParams();
|
|
for (size_t i = 0; i < numParams; i++) {
|
|
const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
|
|
clang::QualType QT = PVD->getOriginalType();
|
|
if (QT->isArrayType()) {
|
|
mContext->ReportError(
|
|
PVD->getTypeSpecStartLoc(),
|
|
"exported function parameters may not have array type: %0")
|
|
<< QT;
|
|
}
|
|
}
|
|
AnnotateFunction(FD);
|
|
}
|
|
}
|
|
|
|
if (getTargetAPI() >= SLANG_FEATURE_SINGLE_SOURCE_API) {
|
|
if (FD && FD->hasBody() && !FD->isImplicit() &&
|
|
!Slang::IsLocInRSHeaderFile(FD->getLocation(), mSourceMgr)) {
|
|
if (FD->hasAttr<clang::RenderScriptKernelAttr>()) {
|
|
// Log functions with attribute "kernel" by their names, and assign
|
|
// them slot numbers. Any other function cannot be used in a
|
|
// rsForEach() or rsForEachWithOptions() call, including old-style
|
|
// kernel functions which are defined without the "kernel" attribute.
|
|
mContext->addForEach(FD);
|
|
}
|
|
// Look for any kernel launch calls and translate them into using the
|
|
// internal API.
|
|
// Report a compiler error on kernel launches inside a kernel.
|
|
mForEachHandler.handleForEachCalls(FD, getTargetAPI());
|
|
}
|
|
}
|
|
}
|
|
|
|
return mGen->HandleTopLevelDecl(D);
|
|
}
|
|
|
|
void Backend::HandleTranslationUnitPre(clang::ASTContext &C) {
|
|
clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
|
|
|
|
if (!mContext->processReducePragmas(this))
|
|
return;
|
|
|
|
// If we have an invalid RS/FS AST, don't check further.
|
|
if (!mASTChecker.Validate()) {
|
|
return;
|
|
}
|
|
|
|
if (mIsFilterscript) {
|
|
mContext->addPragma("rs_fp_relaxed", "");
|
|
}
|
|
|
|
int version = mContext->getVersion();
|
|
if (version == 0) {
|
|
// Not setting a version is an error
|
|
mDiagEngine.Report(
|
|
mSourceMgr.getLocForEndOfFile(mSourceMgr.getMainFileID()),
|
|
mDiagEngine.getCustomDiagID(
|
|
clang::DiagnosticsEngine::Error,
|
|
"missing pragma for version in source file"));
|
|
} else {
|
|
slangAssert(version == 1);
|
|
}
|
|
|
|
if (mContext->getReflectJavaPackageName().empty()) {
|
|
mDiagEngine.Report(
|
|
mSourceMgr.getLocForEndOfFile(mSourceMgr.getMainFileID()),
|
|
mDiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
|
|
"missing \"#pragma rs "
|
|
"java_package_name(com.foo.bar)\" "
|
|
"in source file"));
|
|
return;
|
|
}
|
|
|
|
// Create a static global destructor if necessary (to handle RS object
|
|
// runtime cleanup).
|
|
clang::FunctionDecl *FD = mRefCount.CreateStaticGlobalDtor();
|
|
if (FD) {
|
|
HandleTopLevelDecl(clang::DeclGroupRef(FD));
|
|
}
|
|
|
|
// Process any static function declarations
|
|
for (clang::DeclContext::decl_iterator I = TUDecl->decls_begin(),
|
|
E = TUDecl->decls_end(); I != E; I++) {
|
|
if ((I->getKind() >= clang::Decl::firstFunction) &&
|
|
(I->getKind() <= clang::Decl::lastFunction)) {
|
|
clang::FunctionDecl *FD = llvm::dyn_cast<clang::FunctionDecl>(*I);
|
|
if (FD && !FD->isGlobal()) {
|
|
AnnotateFunction(FD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void Backend::dumpExportVarInfo(llvm::Module *M) {
|
|
int slotCount = 0;
|
|
if (mExportVarMetadata == nullptr)
|
|
mExportVarMetadata = M->getOrInsertNamedMetadata(RS_EXPORT_VAR_MN);
|
|
|
|
llvm::SmallVector<llvm::Metadata *, 2> ExportVarInfo;
|
|
|
|
// We emit slot information (#rs_object_slots) for any reference counted
|
|
// RS type or pointer (which can also be bound).
|
|
|
|
for (RSContext::const_export_var_iterator I = mContext->export_vars_begin(),
|
|
E = mContext->export_vars_end();
|
|
I != E;
|
|
I++) {
|
|
const RSExportVar *EV = *I;
|
|
const RSExportType *ET = EV->getType();
|
|
bool countsAsRSObject = false;
|
|
|
|
// Variable name
|
|
ExportVarInfo.push_back(
|
|
llvm::MDString::get(mLLVMContext, EV->getName().c_str()));
|
|
|
|
// Type name
|
|
switch (ET->getClass()) {
|
|
case RSExportType::ExportClassPrimitive: {
|
|
const RSExportPrimitiveType *PT =
|
|
static_cast<const RSExportPrimitiveType*>(ET);
|
|
ExportVarInfo.push_back(
|
|
llvm::MDString::get(
|
|
mLLVMContext, llvm::utostr(PT->getType())));
|
|
if (PT->isRSObjectType()) {
|
|
countsAsRSObject = true;
|
|
}
|
|
break;
|
|
}
|
|
case RSExportType::ExportClassPointer: {
|
|
ExportVarInfo.push_back(
|
|
llvm::MDString::get(
|
|
mLLVMContext, ("*" + static_cast<const RSExportPointerType*>(ET)
|
|
->getPointeeType()->getName()).c_str()));
|
|
break;
|
|
}
|
|
case RSExportType::ExportClassMatrix: {
|
|
ExportVarInfo.push_back(
|
|
llvm::MDString::get(
|
|
mLLVMContext, llvm::utostr(
|
|
/* TODO Strange value. This pushes just a number, quite
|
|
* different than the other cases. What is this used for?
|
|
* These are the metadata values that some partner drivers
|
|
* want to reference (for TBAA, etc.). We may want to look
|
|
* at whether these provide any reasonable value (or have
|
|
* distinct enough values to actually depend on).
|
|
*/
|
|
DataTypeRSMatrix2x2 +
|
|
static_cast<const RSExportMatrixType*>(ET)->getDim() - 2)));
|
|
break;
|
|
}
|
|
case RSExportType::ExportClassVector:
|
|
case RSExportType::ExportClassConstantArray:
|
|
case RSExportType::ExportClassRecord: {
|
|
ExportVarInfo.push_back(
|
|
llvm::MDString::get(mLLVMContext,
|
|
EV->getType()->getName().c_str()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
mExportVarMetadata->addOperand(
|
|
llvm::MDNode::get(mLLVMContext, ExportVarInfo));
|
|
ExportVarInfo.clear();
|
|
|
|
if (mRSObjectSlotsMetadata == nullptr) {
|
|
mRSObjectSlotsMetadata =
|
|
M->getOrInsertNamedMetadata(RS_OBJECT_SLOTS_MN);
|
|
}
|
|
|
|
if (countsAsRSObject) {
|
|
mRSObjectSlotsMetadata->addOperand(llvm::MDNode::get(mLLVMContext,
|
|
llvm::MDString::get(mLLVMContext, llvm::utostr(slotCount))));
|
|
}
|
|
|
|
slotCount++;
|
|
}
|
|
}
|
|
|
|
// A similar algorithm is present in Backend::PadStruct().
|
|
static void PadHelperFunctionStruct(llvm::Module *M,
|
|
llvm::StructType **paddedStructType,
|
|
std::vector<unsigned> *origFieldNumToPaddedFieldNum,
|
|
llvm::StructType *origStructType) {
|
|
slangAssert(origFieldNumToPaddedFieldNum->empty());
|
|
origFieldNumToPaddedFieldNum->resize(2 * origStructType->getNumElements());
|
|
|
|
llvm::LLVMContext &llvmContext = M->getContext();
|
|
|
|
const llvm::DataLayout *DL = &M->getDataLayout();
|
|
const llvm::StructLayout *SL = DL->getStructLayout(origStructType);
|
|
|
|
// Field types -- including any padding fields we need to insert.
|
|
std::vector<llvm::Type *> paddedFieldTypes;
|
|
paddedFieldTypes.reserve(2 * origStructType->getNumElements());
|
|
|
|
// Is there any padding in this struct?
|
|
bool foundPadding = false;
|
|
|
|
llvm::Type *const byteType = llvm::Type::getInt8Ty(llvmContext);
|
|
unsigned origFieldNum = 0, paddedFieldNum = 0;
|
|
uint64_t fieldPrePaddingOffset = 0; // byte offset of pre-field padding within struct
|
|
for (llvm::Type *fieldType : origStructType->elements()) {
|
|
const uint64_t fieldOffset = SL->getElementOffset(origFieldNum);
|
|
const size_t prePadding = fieldOffset - fieldPrePaddingOffset;
|
|
if (prePadding != 0) {
|
|
foundPadding = true;
|
|
paddedFieldTypes.push_back(llvm::ArrayType::get(byteType, prePadding));
|
|
++paddedFieldNum;
|
|
}
|
|
paddedFieldTypes.push_back(fieldType);
|
|
(*origFieldNumToPaddedFieldNum)[origFieldNum] = paddedFieldNum;
|
|
|
|
// get ready for the next field
|
|
fieldPrePaddingOffset = fieldOffset + DL->getTypeAllocSize(fieldType);
|
|
++origFieldNum;
|
|
++paddedFieldNum;
|
|
}
|
|
|
|
// In order to ensure that the front end (including reflected code)
|
|
// and back end agree on struct size (not just field offsets) we may
|
|
// need to add explicit tail padding, just as we'e added explicit
|
|
// padding between fields.
|
|
slangAssert(SL->getSizeInBytes() >= fieldPrePaddingOffset);
|
|
if (const size_t tailPadding = SL->getSizeInBytes() - fieldPrePaddingOffset) {
|
|
foundPadding = true;
|
|
paddedFieldTypes.push_back(llvm::ArrayType::get(byteType, tailPadding));
|
|
}
|
|
|
|
*paddedStructType = (foundPadding
|
|
? llvm::StructType::get(llvmContext, paddedFieldTypes)
|
|
: origStructType);
|
|
}
|
|
|
|
void Backend::dumpExportFunctionInfo(llvm::Module *M) {
|
|
if (mExportFuncMetadata == nullptr)
|
|
mExportFuncMetadata =
|
|
M->getOrInsertNamedMetadata(RS_EXPORT_FUNC_MN);
|
|
|
|
llvm::SmallVector<llvm::Metadata *, 1> ExportFuncInfo;
|
|
|
|
for (RSContext::const_export_func_iterator
|
|
I = mContext->export_funcs_begin(),
|
|
E = mContext->export_funcs_end();
|
|
I != E;
|
|
I++) {
|
|
const RSExportFunc *EF = *I;
|
|
|
|
// Function name
|
|
if (!EF->hasParam()) {
|
|
ExportFuncInfo.push_back(llvm::MDString::get(mLLVMContext,
|
|
EF->getName().c_str()));
|
|
} else {
|
|
llvm::Function *F = M->getFunction(EF->getName());
|
|
llvm::Function *HelperFunction;
|
|
const std::string HelperFunctionName(".helper_" + EF->getName());
|
|
|
|
slangAssert(F && "Function marked as exported disappeared in Bitcode");
|
|
|
|
// Create helper function
|
|
{
|
|
llvm::StructType *OrigHelperFunctionParameterTy = nullptr;
|
|
llvm::StructType *PaddedHelperFunctionParameterTy = nullptr;
|
|
|
|
std::vector<unsigned> OrigFieldNumToPaddedFieldNum;
|
|
std::vector<bool> isPassedViaPtr;
|
|
|
|
if (!F->getArgumentList().empty()) {
|
|
std::vector<llvm::Type*> HelperFunctionParameterTys;
|
|
for (llvm::Function::arg_iterator AI = F->arg_begin(),
|
|
AE = F->arg_end();
|
|
AI != AE; AI++) {
|
|
if (AI->getType()->isPointerTy() &&
|
|
AI->getType()->getPointerElementType()->isStructTy()) {
|
|
HelperFunctionParameterTys.push_back(
|
|
AI->getType()->getPointerElementType());
|
|
isPassedViaPtr.push_back(true);
|
|
} else {
|
|
// on 64-bit architecture(s), a vector type could be too big
|
|
// to be passed in a register and instead passed
|
|
// via a pointer to a temporary copy
|
|
llvm::Type *Ty = AI->getType();
|
|
bool viaPtr = false;
|
|
if (Ty->isPointerTy() && Ty->getPointerElementType()) {
|
|
Ty = Ty->getPointerElementType();
|
|
viaPtr = true;
|
|
}
|
|
HelperFunctionParameterTys.push_back(Ty);
|
|
isPassedViaPtr.push_back(viaPtr);
|
|
}
|
|
}
|
|
OrigHelperFunctionParameterTy =
|
|
llvm::StructType::get(mLLVMContext, HelperFunctionParameterTys);
|
|
PadHelperFunctionStruct(M,
|
|
&PaddedHelperFunctionParameterTy, &OrigFieldNumToPaddedFieldNum,
|
|
OrigHelperFunctionParameterTy);
|
|
}
|
|
|
|
if (!EF->checkParameterPacketType(OrigHelperFunctionParameterTy)) {
|
|
fprintf(stderr, "Failed to export function %s: parameter type "
|
|
"mismatch during creation of helper function.\n",
|
|
EF->getName().c_str());
|
|
|
|
const RSExportRecordType *Expected = EF->getParamPacketType();
|
|
if (Expected) {
|
|
fprintf(stderr, "Expected:\n");
|
|
Expected->getLLVMType()->dump();
|
|
}
|
|
if (OrigHelperFunctionParameterTy) {
|
|
fprintf(stderr, "Got:\n");
|
|
OrigHelperFunctionParameterTy->dump();
|
|
}
|
|
abort();
|
|
}
|
|
|
|
std::vector<llvm::Type*> Params;
|
|
if (PaddedHelperFunctionParameterTy) {
|
|
llvm::PointerType *HelperFunctionParameterTyP =
|
|
llvm::PointerType::getUnqual(PaddedHelperFunctionParameterTy);
|
|
Params.push_back(HelperFunctionParameterTyP);
|
|
}
|
|
|
|
llvm::FunctionType * HelperFunctionType =
|
|
llvm::FunctionType::get(F->getReturnType(),
|
|
Params,
|
|
/* IsVarArgs = */false);
|
|
|
|
HelperFunction =
|
|
llvm::Function::Create(HelperFunctionType,
|
|
llvm::GlobalValue::ExternalLinkage,
|
|
HelperFunctionName,
|
|
M);
|
|
|
|
HelperFunction->addFnAttr(llvm::Attribute::NoInline);
|
|
HelperFunction->setCallingConv(F->getCallingConv());
|
|
|
|
// Create helper function body
|
|
{
|
|
llvm::Argument *HelperFunctionParameter =
|
|
&(*HelperFunction->arg_begin());
|
|
llvm::BasicBlock *BB =
|
|
llvm::BasicBlock::Create(mLLVMContext, "entry", HelperFunction);
|
|
llvm::IRBuilder<> *IB = new llvm::IRBuilder<>(BB);
|
|
llvm::SmallVector<llvm::Value*, 6> Params;
|
|
llvm::Value *Idx[2];
|
|
|
|
Idx[0] =
|
|
llvm::ConstantInt::get(llvm::Type::getInt32Ty(mLLVMContext), 0);
|
|
|
|
// getelementptr and load instruction for all elements in
|
|
// parameter .p
|
|
for (size_t origFieldNum = 0; origFieldNum < EF->getNumParameters(); origFieldNum++) {
|
|
// getelementptr
|
|
Idx[1] = llvm::ConstantInt::get(
|
|
llvm::Type::getInt32Ty(mLLVMContext), OrigFieldNumToPaddedFieldNum[origFieldNum]);
|
|
|
|
llvm::Value *Ptr = NULL;
|
|
|
|
Ptr = IB->CreateInBoundsGEP(HelperFunctionParameter, Idx);
|
|
|
|
// Load is only required for non-struct ptrs
|
|
if (isPassedViaPtr[origFieldNum]) {
|
|
Params.push_back(Ptr);
|
|
} else {
|
|
llvm::Value *V = IB->CreateLoad(Ptr);
|
|
Params.push_back(V);
|
|
}
|
|
}
|
|
|
|
// Call and pass the all elements as parameter to F
|
|
llvm::CallInst *CI = IB->CreateCall(F, Params);
|
|
|
|
CI->setCallingConv(F->getCallingConv());
|
|
|
|
if (F->getReturnType() == llvm::Type::getVoidTy(mLLVMContext)) {
|
|
IB->CreateRetVoid();
|
|
} else {
|
|
IB->CreateRet(CI);
|
|
}
|
|
|
|
delete IB;
|
|
}
|
|
}
|
|
|
|
ExportFuncInfo.push_back(
|
|
llvm::MDString::get(mLLVMContext, HelperFunctionName.c_str()));
|
|
}
|
|
|
|
mExportFuncMetadata->addOperand(
|
|
llvm::MDNode::get(mLLVMContext, ExportFuncInfo));
|
|
ExportFuncInfo.clear();
|
|
}
|
|
}
|
|
|
|
void Backend::dumpExportForEachInfo(llvm::Module *M) {
|
|
if (mExportForEachNameMetadata == nullptr) {
|
|
mExportForEachNameMetadata =
|
|
M->getOrInsertNamedMetadata(RS_EXPORT_FOREACH_NAME_MN);
|
|
}
|
|
if (mExportForEachSignatureMetadata == nullptr) {
|
|
mExportForEachSignatureMetadata =
|
|
M->getOrInsertNamedMetadata(RS_EXPORT_FOREACH_MN);
|
|
}
|
|
|
|
llvm::SmallVector<llvm::Metadata *, 1> ExportForEachName;
|
|
llvm::SmallVector<llvm::Metadata *, 1> ExportForEachInfo;
|
|
|
|
for (RSContext::const_export_foreach_iterator
|
|
I = mContext->export_foreach_begin(),
|
|
E = mContext->export_foreach_end();
|
|
I != E;
|
|
I++) {
|
|
const RSExportForEach *EFE = *I;
|
|
|
|
ExportForEachName.push_back(
|
|
llvm::MDString::get(mLLVMContext, EFE->getName().c_str()));
|
|
|
|
mExportForEachNameMetadata->addOperand(
|
|
llvm::MDNode::get(mLLVMContext, ExportForEachName));
|
|
ExportForEachName.clear();
|
|
|
|
ExportForEachInfo.push_back(
|
|
llvm::MDString::get(mLLVMContext,
|
|
llvm::utostr(EFE->getSignatureMetadata())));
|
|
|
|
mExportForEachSignatureMetadata->addOperand(
|
|
llvm::MDNode::get(mLLVMContext, ExportForEachInfo));
|
|
ExportForEachInfo.clear();
|
|
}
|
|
}
|
|
|
|
void Backend::dumpExportReduceInfo(llvm::Module *M) {
|
|
if (!mExportReduceMetadata) {
|
|
mExportReduceMetadata =
|
|
M->getOrInsertNamedMetadata(RS_EXPORT_REDUCE_MN);
|
|
}
|
|
|
|
llvm::SmallVector<llvm::Metadata *, 6> ExportReduceInfo;
|
|
// Add operand to ExportReduceInfo, padding out missing operands with
|
|
// nullptr.
|
|
auto addOperand = [&ExportReduceInfo](uint32_t Idx, llvm::Metadata *N) {
|
|
while (Idx > ExportReduceInfo.size())
|
|
ExportReduceInfo.push_back(nullptr);
|
|
ExportReduceInfo.push_back(N);
|
|
};
|
|
// Add string operand to ExportReduceInfo, padding out missing operands
|
|
// with nullptr.
|
|
// If string is empty, then do not add it unless Always is true.
|
|
auto addString = [&addOperand, this](uint32_t Idx, const std::string &S,
|
|
bool Always = true) {
|
|
if (Always || !S.empty())
|
|
addOperand(Idx, llvm::MDString::get(mLLVMContext, S));
|
|
};
|
|
|
|
// Add the description of the reduction kernels to the metadata node.
|
|
for (auto I = mContext->export_reduce_begin(),
|
|
E = mContext->export_reduce_end();
|
|
I != E; ++I) {
|
|
ExportReduceInfo.clear();
|
|
|
|
int Idx = 0;
|
|
|
|
addString(Idx++, (*I)->getNameReduce());
|
|
|
|
addOperand(Idx++, llvm::MDString::get(mLLVMContext, llvm::utostr((*I)->getAccumulatorTypeSize())));
|
|
|
|
llvm::SmallVector<llvm::Metadata *, 2> Accumulator;
|
|
Accumulator.push_back(
|
|
llvm::MDString::get(mLLVMContext, (*I)->getNameAccumulator()));
|
|
Accumulator.push_back(llvm::MDString::get(
|
|
mLLVMContext,
|
|
llvm::utostr((*I)->getAccumulatorSignatureMetadata())));
|
|
addOperand(Idx++, llvm::MDTuple::get(mLLVMContext, Accumulator));
|
|
|
|
addString(Idx++, (*I)->getNameInitializer(), false);
|
|
addString(Idx++, (*I)->getNameCombiner(), false);
|
|
addString(Idx++, (*I)->getNameOutConverter(), false);
|
|
addString(Idx++, (*I)->getNameHalter(), false);
|
|
|
|
mExportReduceMetadata->addOperand(
|
|
llvm::MDTuple::get(mLLVMContext, ExportReduceInfo));
|
|
}
|
|
}
|
|
|
|
void Backend::dumpExportTypeInfo(llvm::Module *M) {
|
|
llvm::SmallVector<llvm::Metadata *, 1> ExportTypeInfo;
|
|
|
|
for (RSContext::const_export_type_iterator
|
|
I = mContext->export_types_begin(),
|
|
E = mContext->export_types_end();
|
|
I != E;
|
|
I++) {
|
|
// First, dump type name list to export
|
|
const RSExportType *ET = I->getValue();
|
|
|
|
ExportTypeInfo.clear();
|
|
// Type name
|
|
ExportTypeInfo.push_back(
|
|
llvm::MDString::get(mLLVMContext, ET->getName().c_str()));
|
|
|
|
if (ET->getClass() == RSExportType::ExportClassRecord) {
|
|
const RSExportRecordType *ERT =
|
|
static_cast<const RSExportRecordType*>(ET);
|
|
|
|
if (mExportTypeMetadata == nullptr)
|
|
mExportTypeMetadata =
|
|
M->getOrInsertNamedMetadata(RS_EXPORT_TYPE_MN);
|
|
|
|
mExportTypeMetadata->addOperand(
|
|
llvm::MDNode::get(mLLVMContext, ExportTypeInfo));
|
|
|
|
// Now, export struct field information to %[struct name]
|
|
std::string StructInfoMetadataName("%");
|
|
StructInfoMetadataName.append(ET->getName());
|
|
llvm::NamedMDNode *StructInfoMetadata =
|
|
M->getOrInsertNamedMetadata(StructInfoMetadataName);
|
|
llvm::SmallVector<llvm::Metadata *, 3> FieldInfo;
|
|
|
|
slangAssert(StructInfoMetadata->getNumOperands() == 0 &&
|
|
"Metadata with same name was created before");
|
|
for (RSExportRecordType::const_field_iterator FI = ERT->fields_begin(),
|
|
FE = ERT->fields_end();
|
|
FI != FE;
|
|
FI++) {
|
|
const RSExportRecordType::Field *F = *FI;
|
|
|
|
// 1. field name
|
|
FieldInfo.push_back(llvm::MDString::get(mLLVMContext,
|
|
F->getName().c_str()));
|
|
|
|
// 2. field type name
|
|
FieldInfo.push_back(
|
|
llvm::MDString::get(mLLVMContext,
|
|
F->getType()->getName().c_str()));
|
|
|
|
StructInfoMetadata->addOperand(
|
|
llvm::MDNode::get(mLLVMContext, FieldInfo));
|
|
FieldInfo.clear();
|
|
}
|
|
} // ET->getClass() == RSExportType::ExportClassRecord
|
|
}
|
|
}
|
|
|
|
void Backend::HandleTranslationUnitPost(llvm::Module *M) {
|
|
|
|
if (!mContext->is64Bit()) {
|
|
M->setDataLayout("e-p:32:32-i64:64-v128:64:128-n32-S64");
|
|
}
|
|
|
|
if (!mContext->processExports())
|
|
return;
|
|
|
|
if (mContext->hasExportVar())
|
|
dumpExportVarInfo(M);
|
|
|
|
if (mContext->hasExportFunc())
|
|
dumpExportFunctionInfo(M);
|
|
|
|
if (mContext->hasExportForEach())
|
|
dumpExportForEachInfo(M);
|
|
|
|
if (mContext->hasExportReduce())
|
|
dumpExportReduceInfo(M);
|
|
|
|
if (mContext->hasExportType())
|
|
dumpExportTypeInfo(M);
|
|
}
|
|
|
|
} // namespace slang
|