blob: 036ba1c98800eba32011a952bb0d1b56cf925a5c [file] [log] [blame] [edit]
//===- StripAttributes.cpp - Remove attributes not supported by PNaCl------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass strips out attributes that are not supported by PNaCl's
// stable ABI. Currently, this strips out:
//
// * Function and argument attributes from functions and function
// calls.
// * Calling conventions from functions and function calls.
// * The "align" attribute on functions.
// * The "unnamed_addr" attribute on functions and global variables.
// * The distinction between "internal" and "private" linkage.
// * "protected" and "internal" visibility of functions and globals.
// * All sections are stripped. A few sections cause warnings.
// * The arithmetic attributes "nsw", "nuw" and "exact".
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "llvm/Pass.h"
#include "llvm/IR/CallSite.h"
#include "llvm/Transforms/NaCl.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace {
// This is a ModulePass so that it can modify attributes of global
// variables.
class StripAttributes : public ModulePass {
public:
static char ID; // Pass identification, replacement for typeid
StripAttributes() : ModulePass(ID) {
initializeStripAttributesPass(*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override;
};
}
char StripAttributes::ID = 0;
INITIALIZE_PASS(StripAttributes, "nacl-strip-attributes",
"Strip out attributes that are not part of PNaCl's ABI",
false, false)
static void CheckAttributes(AttributeSet Attrs) {
for (unsigned Slot = 0; Slot < Attrs.getNumSlots(); ++Slot) {
for (AttributeSet::iterator Attr = Attrs.begin(Slot), E = Attrs.end(Slot);
Attr != E; ++Attr) {
if (!Attr->isEnumAttribute()) {
continue;
}
switch (Attr->getKindAsEnum()) {
// The vast majority of attributes are hints that can safely
// be removed, so don't complain if we see attributes we don't
// recognize.
default:
// The following attributes can affect calling conventions.
// Rather than complaining, we just strip these out.
// ExpandSmallArguments should have rendered SExt/ZExt
// meaningless since the function arguments will be at least
// 32-bit.
case Attribute::InReg:
case Attribute::SExt:
case Attribute::ZExt:
// These attributes influence ABI decisions that should not be
// visible to PNaCl pexes.
case Attribute::NonLazyBind: // Only relevant to dynamic linking.
case Attribute::NoRedZone:
case Attribute::StackAlignment:
// The following attributes are just hints, which can be
// safely removed.
case Attribute::AlwaysInline:
case Attribute::InlineHint:
case Attribute::MinSize:
case Attribute::NoAlias:
case Attribute::NoBuiltin:
case Attribute::NoCapture:
case Attribute::NoDuplicate:
case Attribute::NoImplicitFloat:
case Attribute::NoInline:
case Attribute::NoReturn:
case Attribute::OptimizeForSize:
case Attribute::ReadNone:
case Attribute::ReadOnly:
// PNaCl does not support -fstack-protector in the translator.
case Attribute::StackProtect:
case Attribute::StackProtectReq:
case Attribute::StackProtectStrong:
// PNaCl does not support ASan in the translator.
case Attribute::SanitizeAddress:
case Attribute::SanitizeThread:
case Attribute::SanitizeMemory:
// The Language References cites setjmp() as an example of a
// function which returns twice, and says ReturnsTwice is
// necessary to disable optimizations such as tail calls.
// However, in the PNaCl ABI, setjmp() is an intrinsic, and
// user-defined functions are not allowed to return twice.
case Attribute::ReturnsTwice:
// NoUnwind is not a hint if it causes unwind info to be
// omitted, since this will prevent C++ exceptions from
// propagating. In the future, when PNaCl supports zero-cost
// C++ exception handling using unwind info, we might allow
// NoUnwind and UWTable. Alternatively, we might continue to
// disallow them, and just generate unwind info for all
// functions.
case Attribute::NoUnwind:
case Attribute::UWTable:
break;
// A few attributes can change program behaviour if removed,
// so check for these.
case Attribute::ByVal:
case Attribute::StructRet:
case Attribute::Alignment:
Attrs.dump();
report_fatal_error(
"Attribute should already have been removed by ExpandByVal");
case Attribute::Naked:
case Attribute::Nest:
Attrs.dump();
report_fatal_error("Unsupported attribute");
}
}
}
}
static const char* ShouldWarnAboutSection(const char* Section) {
static const char* SpecialSections[] = {
".init_array",
".init",
".fini_array",
".fini",
// Java/LSB:
".jcr",
// LSB:
".ctors",
".dtors",
};
for (auto CheckSection : SpecialSections) {
if (strcmp(Section, CheckSection) == 0) {
return CheckSection;
}
}
return nullptr;
}
void stripGlobalValueAttrs(GlobalValue *GV) {
// In case source code uses __attribute__((visibility("hidden"))) or
// __attribute__((visibility("protected"))), strip these attributes.
GV->setVisibility(GlobalValue::DefaultVisibility);
GV->setUnnamedAddr(false);
if (GV->hasSection()) {
const char *Section = GV->getSection();
// check for a few special cases
if (const char *WarnSection = ShouldWarnAboutSection(Section)) {
errs() << "Warning: " << GV->getName() <<
" will have its section (" <<
WarnSection << ") stripped.\n";
}
if(GlobalObject* GO = dyn_cast<GlobalObject>(GV)) {
GO->setSection("");
}
// Nothing we can do if GV isn't a GlobalObject.
}
// Convert "private" linkage to "internal" to reduce the number of
// linkage types that need to be represented in PNaCl's wire format.
//
// We convert "private" to "internal" rather than vice versa because
// "private" symbols are omitted from the nexe's symbol table, which
// would get in the way of debugging when an unstripped pexe is
// translated offline.
if (GV->getLinkage() == GlobalValue::PrivateLinkage)
GV->setLinkage(GlobalValue::InternalLinkage);
}
void stripFunctionAttrs(DataLayout *DL, Function *F) {
CheckAttributes(F->getAttributes());
F->setAttributes(AttributeSet());
F->setCallingConv(CallingConv::C);
F->setAlignment(0);
for (BasicBlock &BB : *F) {
for (Instruction &I : BB) {
CallSite Call(&I);
if (Call) {
CheckAttributes(Call.getAttributes());
Call.setAttributes(AttributeSet());
Call.setCallingConv(CallingConv::C);
} else if (OverflowingBinaryOperator *Op =
dyn_cast<OverflowingBinaryOperator>(&I)) {
cast<BinaryOperator>(Op)->setHasNoUnsignedWrap(false);
cast<BinaryOperator>(Op)->setHasNoSignedWrap(false);
} else if (PossiblyExactOperator *Op =
dyn_cast<PossiblyExactOperator>(&I)) {
cast<BinaryOperator>(Op)->setIsExact(false);
}
}
}
}
bool StripAttributes::runOnModule(Module &M) {
DataLayout DL(&M);
for (Function &F : M)
// Avoid stripping attributes from intrinsics because the
// constructor for Functions just adds them back again. It would
// be confusing if the attributes were sometimes present on
// intrinsics and sometimes not.
if (!F.isIntrinsic()) {
stripGlobalValueAttrs(&F);
stripFunctionAttrs(&DL, &F);
}
for (GlobalVariable &GV : M.globals())
stripGlobalValueAttrs(&GV);
return true;
}
ModulePass *llvm::createStripAttributesPass() {
return new StripAttributes();
}