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(); |
+} |