blob: b254672ea2a1efc1a2f2dd59a442a01696a2985e [file] [log] [blame]
//===- 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 StructType *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(
InitTemplateType,
TemplateDataVar,
ConstantInt::get(M.getContext(), APInt(32, 1)));
setGlobalVariableValue(M, "__tls_template_tdata_end", TdataEnd);
Constant *TotalEnd = ConstantExpr::getGetElementPtr(
TemplateType,
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 TemplateType;
}
static void rewriteTlsVars(Module &M, std::vector<VarInfo> *TlsVars,
StructType *TemplateType) {
// 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, TemplateType->getPointerTo(), "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(
TemplateType, 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;
StructType *TemplateType = buildTlsTemplate(M, &TlsVars);
rewriteTlsVars(M, &TlsVars, TemplateType);
defineTlsLayoutFunctions(M);
return true;
}
ModulePass *llvm::createExpandTlsPass() {
return new ExpandTls();
}