blob: ca045e89139ca95f8dbbfdc47127b6cab3ccf949 [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.
//
// Layout:
//
// This currently uses an x86-style layout where the TLS variables are
// placed at addresses below the thread pointer (i.e. with negative offsets
// from the thread pointer). See
// native_client/src/untrusted/nacl/tls_params.h for an explanation of
// different architectures' TLS layouts.
//
// This x86-style layout is *not* required for normal ABI-stable pexes.
//
// However, the x86-style layout is currently required for Non-SFI pexes
// that call x86 Linux syscalls directly. This is a configuration that is
// only used for testing.
//
// Before PNaCl was launched, using x86-style layout used to be required
// because there was a check in nacl_irt_thread_create() (in
// irt/irt_thread.c) that required the thread pointer to be a self-pointer
// on x86-32. That requirement was removed because it was non-portable
// (because it could cause a pexe to fail on x86 but not on ARM) -- see
// https://codereview.chromium.org/11411310.
//
//===----------------------------------------------------------------------===//
#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;
// Offset of the TLS variable. Initially this is a non-negative offset
// from the start of the TLS block. After we adjust for the x86-style
// layout, this becomes a negative offset from the thread pointer.
uint32_t Offset;
};
struct TlsTemplate {
std::vector<VarInfo> TlsVars;
Constant *Data;
uint32_t DataSize;
uint32_t TotalSize;
uint32_t Alignment;
};
class PassState {
public:
PassState(Module *M): M(M), DL(M), Offset(0), Alignment(1) {}
Module *M;
DataLayout DL;
uint32_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<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);
if (FieldValues)
FieldValues->push_back(Constant::getNullValue(PadType));
State->Offset += PadSize;
}
if (State->Alignment < Alignment) {
State->Alignment = Alignment;
}
}
static uint32_t addVarToTlsTemplate(PassState *State,
std::vector<Constant*> *FieldValues,
GlobalVariable *TlsVar) {
unsigned Alignment = State->DL.getPreferredAlignment(TlsVar);
padToAlignment(State, FieldValues, Alignment);
if (FieldValues)
FieldValues->push_back(TlsVar->getInitializer());
uint32_t Offset = State->Offset;
State->Offset +=
State->DL.getTypeAllocSize(TlsVar->getType()->getElementType());
return Offset;
}
// This is similar to ConstantStruct::getAnon(), but we give a name to the
// struct type to make the IR output more readable.
static Constant *makeInitStruct(Module &M, ArrayRef<Constant *> Elements) {
SmallVector<Type *, 32> FieldTypes;
FieldTypes.reserve(Elements.size());
for (Constant *Val : Elements)
FieldTypes.push_back(Val->getType());
// We create the TLS template struct as "packed" because we insert
// alignment padding ourselves.
StructType *Ty = StructType::create(M.getContext(), FieldTypes,
"tls_init_template", /*isPacked=*/ true);
return ConstantStruct::get(Ty, Elements);
}
static void buildTlsTemplate(Module &M, TlsTemplate *Result) {
std::vector<Constant*> FieldInitValues;
PassState State(&M);
for (GlobalVariable &GV : M.globals()) {
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()) {
VarInfo Info;
Info.TlsVar = &GV;
Info.Offset = addVarToTlsTemplate(&State, &FieldInitValues, &GV);
Result->TlsVars.push_back(Info);
}
}
}
Result->DataSize = State.Offset;
// Handle zero-initialized TLS variables in a second pass, because
// these should follow non-zero-initialized TLS variables.
for (GlobalVariable &GV : M.globals()) {
if (GV.isThreadLocal() && GV.getInitializer()->isNullValue()) {
VarInfo Info;
Info.TlsVar = &GV;
Info.Offset = addVarToTlsTemplate(&State, NULL, &GV);
Result->TlsVars.push_back(Info);
}
}
Result->TotalSize = State.Offset;
Result->Alignment = State.Alignment;
Result->Data = makeInitStruct(M, FieldInitValues);
}
static void adjustToX86StyleLayout(TlsTemplate *Templ) {
// 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.
Templ->TotalSize = RoundUpToAlignment(Templ->TotalSize, Templ->Alignment);
// Adjust offsets for x86-style layout.
for (VarInfo &VarInfo : Templ->TlsVars)
VarInfo.Offset -= Templ->TotalSize;
}
static void resolveTemplateVars(Module &M, TlsTemplate *Templ) {
// 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";
GlobalVariable *TemplateDataVar =
new GlobalVariable(M, Templ->Data->getType(), /*isConstant=*/true,
GlobalValue::InternalLinkage, Templ->Data);
setGlobalVariableValue(M, StartSymbol, TemplateDataVar);
TemplateDataVar->setName(StartSymbol);
Type *I8 = Type::getInt8Ty(M.getContext());
Constant *TemplateAsI8 = ConstantExpr::getBitCast(TemplateDataVar,
I8->getPointerTo());
Constant *TdataEnd = ConstantExpr::getGetElementPtr(
I8, TemplateAsI8,
ConstantInt::get(M.getContext(), APInt(32, Templ->DataSize)));
setGlobalVariableValue(M, "__tls_template_tdata_end", TdataEnd);
Constant *TotalEnd = ConstantExpr::getGetElementPtr(
I8, TemplateAsI8,
ConstantInt::get(M.getContext(), APInt(32, Templ->TotalSize)));
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, Templ->Alignment)));
setGlobalVariableValue(M, AlignmentSymbol, AlignmentVar);
AlignmentVar->setName(AlignmentSymbol);
}
static void rewriteTlsVars(Module &M, std::vector<VarInfo> *TlsVars) {
// Set up the intrinsic that reads the thread pointer.
Function *ReadTpFunc = Intrinsic::getDeclaration(&M, Intrinsic::nacl_read_tp);
for (VarInfo &VarInfo : *TlsVars) {
GlobalVariable *Var = VarInfo.TlsVar;
while (!Var->use_empty()) {
Use *U = &*Var->use_begin();
Instruction *InsertPt = PhiSafeInsertPt(U);
Value *ThreadPtr = CallInst::Create(ReadTpFunc, "thread_ptr", InsertPt);
Value *IndexList[] = {
ConstantInt::get(M.getContext(), APInt(32, VarInfo.Offset))
};
Value *TlsFieldI8 = GetElementPtrInst::Create(
Type::getInt8Ty(M.getContext()),
ThreadPtr, IndexList, Var->getName() + ".i8", InsertPt);
Value *TlsField = new BitCastInst(TlsFieldI8, Var->getType(),
Var->getName(), InsertPt);
PhiSafeReplaceUses(U, TlsField);
}
Var->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;
TlsTemplate Templ;
buildTlsTemplate(M, &Templ);
adjustToX86StyleLayout(&Templ);
resolveTemplateVars(M, &Templ);
rewriteTlsVars(M, &Templ.TlsVars);
defineTlsLayoutFunctions(M);
return true;
}
ModulePass *llvm::createExpandTlsPass() {
return new ExpandTls();
}