Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(333)

Unified Diff: lib/Transforms/NaCl/ExpandTls.cpp

Issue 939073008: Rebased PNaCl localmods in LLVM to 223109 (Closed)
Patch Set: undo localmod Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/Transforms/NaCl/ExpandStructRegs.cpp ('k') | lib/Transforms/NaCl/ExpandTlsConstantExpr.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/Transforms/NaCl/ExpandTls.cpp
diff --git a/lib/Transforms/NaCl/ExpandTls.cpp b/lib/Transforms/NaCl/ExpandTls.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc7db7c0ba1cba3e72e2b6e4624941b4e70f79f4
--- /dev/null
+++ b/lib/Transforms/NaCl/ExpandTls.cpp
@@ -0,0 +1,334 @@
+//===- ExpandTls.cpp - Convert TLS variables to a concrete layout----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass expands out uses of thread-local (TLS) variables into
+// more primitive operations.
+//
+// A reference to the address of a TLS variable is expanded into code
+// which gets the current thread's thread pointer using
+// @llvm.nacl.read.tp() and adds a fixed offset.
+//
+// This pass allocates the offsets (relative to the thread pointer)
+// that will be used for TLS variables. It sets up the global
+// variables __tls_template_start, __tls_template_end etc. to contain
+// a template for initializing TLS variables' values for each thread.
+// This is a task normally performed by the linker in ELF systems.
+//
+//===----------------------------------------------------------------------===//
+
+#include <vector>
+
+#include "llvm/Pass.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Transforms/NaCl.h"
+
+using namespace llvm;
+
+namespace {
+ struct VarInfo {
+ GlobalVariable *TlsVar;
+ bool IsBss; // Whether variable is in zero-intialized part of template
+ int TemplateIndex;
+ };
+
+ class PassState {
+ public:
+ PassState(Module *M): M(M), DL(M), Offset(0), Alignment(1) {}
+
+ Module *M;
+ DataLayout DL;
+ uint64_t Offset;
+ // 'Alignment' is the maximum variable alignment seen so far, in
+ // bytes. After visiting all TLS variables, this is the overall
+ // alignment required for the TLS template.
+ uint32_t Alignment;
+ };
+
+ class ExpandTls : public ModulePass {
+ public:
+ static char ID; // Pass identification, replacement for typeid
+ ExpandTls() : ModulePass(ID) {
+ initializeExpandTlsPass(*PassRegistry::getPassRegistry());
+ }
+
+ virtual bool runOnModule(Module &M);
+ };
+}
+
+char ExpandTls::ID = 0;
+INITIALIZE_PASS(ExpandTls, "nacl-expand-tls",
+ "Expand out TLS variables and fix TLS variable layout",
+ false, false)
+
+static void setGlobalVariableValue(Module &M, const char *Name,
+ Constant *Value) {
+ if (GlobalVariable *Var = M.getNamedGlobal(Name)) {
+ if (Var->hasInitializer()) {
+ report_fatal_error(std::string("Variable ") + Name +
+ " already has an initializer");
+ }
+ Var->replaceAllUsesWith(ConstantExpr::getBitCast(Value, Var->getType()));
+ Var->eraseFromParent();
+ }
+}
+
+// Insert alignment padding into the TLS template.
+static void padToAlignment(PassState *State,
+ std::vector<Type*> *FieldTypes,
+ std::vector<Constant*> *FieldValues,
+ unsigned Alignment) {
+ if ((State->Offset & (Alignment - 1)) != 0) {
+ unsigned PadSize = Alignment - (State->Offset & (Alignment - 1));
+ Type *i8 = Type::getInt8Ty(State->M->getContext());
+ Type *PadType = ArrayType::get(i8, PadSize);
+ FieldTypes->push_back(PadType);
+ if (FieldValues)
+ FieldValues->push_back(Constant::getNullValue(PadType));
+ State->Offset += PadSize;
+ }
+ if (State->Alignment < Alignment) {
+ State->Alignment = Alignment;
+ }
+}
+
+static void addVarToTlsTemplate(PassState *State,
+ std::vector<Type*> *FieldTypes,
+ std::vector<Constant*> *FieldValues,
+ GlobalVariable *TlsVar) {
+ unsigned Alignment = State->DL.getPreferredAlignment(TlsVar);
+ padToAlignment(State, FieldTypes, FieldValues, Alignment);
+
+ FieldTypes->push_back(TlsVar->getType()->getElementType());
+ if (FieldValues)
+ FieldValues->push_back(TlsVar->getInitializer());
+ State->Offset +=
+ State->DL.getTypeAllocSize(TlsVar->getType()->getElementType());
+}
+
+static PointerType *buildTlsTemplate(Module &M, std::vector<VarInfo> *TlsVars) {
+ std::vector<Type*> FieldBssTypes;
+ std::vector<Type*> FieldInitTypes;
+ std::vector<Constant*> FieldInitValues;
+ PassState State(&M);
+
+ for (Module::global_iterator GV = M.global_begin();
+ GV != M.global_end();
+ ++GV) {
+ if (GV->isThreadLocal()) {
+ if (!GV->hasInitializer()) {
+ // Since this is a whole-program transformation, "extern" TLS
+ // variables are not allowed at this point.
+ report_fatal_error(std::string("TLS variable without an initializer: ")
+ + GV->getName());
+ }
+ if (!GV->getInitializer()->isNullValue()) {
+ addVarToTlsTemplate(&State, &FieldInitTypes,
+ &FieldInitValues, GV);
+ VarInfo Info;
+ Info.TlsVar = GV;
+ Info.IsBss = false;
+ Info.TemplateIndex = FieldInitTypes.size() - 1;
+ TlsVars->push_back(Info);
+ }
+ }
+ }
+ // Handle zero-initialized TLS variables in a second pass, because
+ // these should follow non-zero-initialized TLS variables.
+ for (Module::global_iterator GV = M.global_begin();
+ GV != M.global_end();
+ ++GV) {
+ if (GV->isThreadLocal() && GV->getInitializer()->isNullValue()) {
+ addVarToTlsTemplate(&State, &FieldBssTypes, NULL, GV);
+ VarInfo Info;
+ Info.TlsVar = GV;
+ Info.IsBss = true;
+ Info.TemplateIndex = FieldBssTypes.size() - 1;
+ TlsVars->push_back(Info);
+ }
+ }
+ // Add final alignment padding so that
+ // (struct tls_struct *) __nacl_read_tp() - 1
+ // gives the correct, aligned start of the TLS variables given the
+ // x86-style layout we are using. This requires some more bytes to
+ // be memset() to zero at runtime. This wastage doesn't seem
+ // important gives that we're not trying to optimize packing by
+ // reordering to put similarly-aligned variables together.
+ padToAlignment(&State, &FieldBssTypes, NULL, State.Alignment);
+
+ // We create the TLS template structs as "packed" because we insert
+ // alignment padding ourselves, and LLVM's implicit insertion of
+ // padding would interfere with ours. tls_bss_template can start at
+ // a non-aligned address immediately following the last field in
+ // tls_init_template.
+ StructType *InitTemplateType =
+ StructType::create(M.getContext(), "tls_init_template");
+ InitTemplateType->setBody(FieldInitTypes, /*isPacked=*/true);
+ StructType *BssTemplateType =
+ StructType::create(M.getContext(), "tls_bss_template");
+ BssTemplateType->setBody(FieldBssTypes, /*isPacked=*/true);
+
+ StructType *TemplateType = StructType::create(M.getContext(), "tls_struct");
+ SmallVector<Type*, 2> TemplateTopFields;
+ TemplateTopFields.push_back(InitTemplateType);
+ TemplateTopFields.push_back(BssTemplateType);
+ TemplateType->setBody(TemplateTopFields, /*isPacked=*/true);
+ PointerType *TemplatePtrType = PointerType::get(TemplateType, 0);
+
+ // We define the following symbols, which are the same as those
+ // defined by NaCl's original customized binutils linker scripts:
+ // __tls_template_start
+ // __tls_template_tdata_end
+ // __tls_template_end
+ // We also define __tls_template_alignment, which was not defined by
+ // the original linker scripts.
+
+ const char *StartSymbol = "__tls_template_start";
+ Constant *TemplateData = ConstantStruct::get(InitTemplateType,
+ FieldInitValues);
+ GlobalVariable *TemplateDataVar =
+ new GlobalVariable(M, InitTemplateType, /*isConstant=*/true,
+ GlobalValue::InternalLinkage, TemplateData);
+ setGlobalVariableValue(M, StartSymbol, TemplateDataVar);
+ TemplateDataVar->setName(StartSymbol);
+
+ Constant *TdataEnd = ConstantExpr::getGetElementPtr(
+ TemplateDataVar,
+ ConstantInt::get(M.getContext(), APInt(32, 1)));
+ setGlobalVariableValue(M, "__tls_template_tdata_end", TdataEnd);
+
+ Constant *TotalEnd = ConstantExpr::getGetElementPtr(
+ ConstantExpr::getBitCast(TemplateDataVar, TemplatePtrType),
+ ConstantInt::get(M.getContext(), APInt(32, 1)));
+ setGlobalVariableValue(M, "__tls_template_end", TotalEnd);
+
+ const char *AlignmentSymbol = "__tls_template_alignment";
+ Type *i32 = Type::getInt32Ty(M.getContext());
+ GlobalVariable *AlignmentVar = new GlobalVariable(
+ M, i32, /*isConstant=*/true,
+ GlobalValue::InternalLinkage,
+ ConstantInt::get(M.getContext(), APInt(32, State.Alignment)));
+ setGlobalVariableValue(M, AlignmentSymbol, AlignmentVar);
+ AlignmentVar->setName(AlignmentSymbol);
+
+ return TemplatePtrType;
+}
+
+static void rewriteTlsVars(Module &M, std::vector<VarInfo> *TlsVars,
+ PointerType *TemplatePtrType) {
+ // Set up the intrinsic that reads the thread pointer.
+ Function *ReadTpFunc = Intrinsic::getDeclaration(&M, Intrinsic::nacl_read_tp);
+
+ for (std::vector<VarInfo>::iterator VarInfo = TlsVars->begin();
+ VarInfo != TlsVars->end();
+ ++VarInfo) {
+ GlobalVariable *Var = VarInfo->TlsVar;
+ while (Var->hasNUsesOrMore(1)) {
+ Use *U = &*Var->use_begin();
+ Instruction *InsertPt = PhiSafeInsertPt(U);
+ Value *RawThreadPtr = CallInst::Create(ReadTpFunc, "tls_raw", InsertPt);
+ Value *TypedThreadPtr = new BitCastInst(RawThreadPtr, TemplatePtrType,
+ "tls_struct", InsertPt);
+ SmallVector<Value*, 3> Indexes;
+ // We use -1 because we use the x86-style TLS layout in which
+ // the TLS data is stored at addresses below the thread pointer.
+ // This is largely because a check in nacl_irt_thread_create()
+ // in irt/irt_thread.c requires the thread pointer to be a
+ // self-pointer on x86-32.
+ // TODO(mseaborn): I intend to remove that check because it is
+ // non-portable. In the mean time, we want PNaCl pexes to work
+ // in older Chromium releases when translated to nexes.
+ Indexes.push_back(ConstantInt::get(
+ M.getContext(), APInt(32, -1)));
+ Indexes.push_back(ConstantInt::get(
+ M.getContext(), APInt(32, VarInfo->IsBss ? 1 : 0)));
+ Indexes.push_back(ConstantInt::get(
+ M.getContext(), APInt(32, VarInfo->TemplateIndex)));
+ Value *TlsField = GetElementPtrInst::Create(TypedThreadPtr, Indexes,
+ "field", InsertPt);
+ PhiSafeReplaceUses(U, TlsField);
+ }
+ VarInfo->TlsVar->eraseFromParent();
+ }
+}
+
+static void replaceFunction(Module &M, const char *Name, Value *NewFunc) {
+ if (Function *Func = M.getFunction(Name)) {
+ if (Func->hasLocalLinkage())
+ return;
+ if (!Func->isDeclaration())
+ report_fatal_error(std::string("Function already defined: ") + Name);
+ Func->replaceAllUsesWith(NewFunc);
+ Func->eraseFromParent();
+ }
+}
+
+// Provide fixed definitions for NaCl's TLS layout functions,
+// __nacl_tp_*(). We adopt the x86-style layout: ExpandTls will
+// output a program that uses the x86-style layout wherever it runs.
+//
+// This overrides the architecture-specific definitions of
+// __nacl_tp_*() that PNaCl's native support code makes available to
+// non-ABI-stable code.
+static void defineTlsLayoutFunctions(Module &M) {
+ Type *i32 = Type::getInt32Ty(M.getContext());
+ SmallVector<Type*, 1> ArgTypes;
+ ArgTypes.push_back(i32);
+ FunctionType *FuncType = FunctionType::get(i32, ArgTypes, /*isVarArg=*/false);
+ Function *NewFunc;
+ BasicBlock *BB;
+
+ // Define the function as follows:
+ // uint32_t __nacl_tp_tdb_offset(uint32_t tdb_size) {
+ // return 0;
+ // }
+ // This means the thread pointer points to the TDB.
+ NewFunc = Function::Create(FuncType, GlobalValue::InternalLinkage,
+ "nacl_tp_tdb_offset", &M);
+ BB = BasicBlock::Create(M.getContext(), "entry", NewFunc);
+ ReturnInst::Create(M.getContext(),
+ ConstantInt::get(M.getContext(), APInt(32, 0)), BB);
+ replaceFunction(M, "__nacl_tp_tdb_offset", NewFunc);
+
+ // Define the function as follows:
+ // uint32_t __nacl_tp_tls_offset(uint32_t tls_size) {
+ // return -tls_size;
+ // }
+ // This means the TLS variables are stored below the thread pointer.
+ NewFunc = Function::Create(FuncType, GlobalValue::InternalLinkage,
+ "nacl_tp_tls_offset", &M);
+ BB = BasicBlock::Create(M.getContext(), "entry", NewFunc);
+ Value *Arg = NewFunc->arg_begin();
+ Arg->setName("size");
+ Value *Result = BinaryOperator::CreateNeg(Arg, "result", BB);
+ ReturnInst::Create(M.getContext(), Result, BB);
+ replaceFunction(M, "__nacl_tp_tls_offset", NewFunc);
+}
+
+bool ExpandTls::runOnModule(Module &M) {
+ ModulePass *Pass = createExpandTlsConstantExprPass();
+ Pass->runOnModule(M);
+ delete Pass;
+
+ std::vector<VarInfo> TlsVars;
+ PointerType *TemplatePtrType = buildTlsTemplate(M, &TlsVars);
+ rewriteTlsVars(M, &TlsVars, TemplatePtrType);
+
+ defineTlsLayoutFunctions(M);
+
+ return true;
+}
+
+ModulePass *llvm::createExpandTlsPass() {
+ return new ExpandTls();
+}
« no previous file with comments | « lib/Transforms/NaCl/ExpandStructRegs.cpp ('k') | lib/Transforms/NaCl/ExpandTlsConstantExpr.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698