| Index: lib/Transforms/NaCl/StripAttributes.cpp
|
| diff --git a/lib/Transforms/NaCl/StripAttributes.cpp b/lib/Transforms/NaCl/StripAttributes.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e88a65c002a72308eb8e9ed7519e93eb9bd27c4c
|
| --- /dev/null
|
| +++ b/lib/Transforms/NaCl/StripAttributes.cpp
|
| @@ -0,0 +1,284 @@
|
| +//===- 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 alignment argument of memcpy/memmove/memset intrinsic calls.
|
| +// * 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".
|
| +// * It reduces the set of possible "align" attributes on memory
|
| +// accesses.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +
|
| +#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());
|
| + }
|
| +
|
| + virtual bool runOnModule(Module &M);
|
| + };
|
| +}
|
| +
|
| +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);
|
| +}
|
| +
|
| +static unsigned normalizeAlignment(DataLayout *DL, unsigned Alignment,
|
| + Type *Ty, bool IsAtomic) {
|
| + unsigned MaxAllowed = 1;
|
| + if (isa<VectorType>(Ty))
|
| + // Already handled properly by FixVectorLoadStoreAlignment.
|
| + return Alignment;
|
| + if (Ty->isDoubleTy() || Ty->isFloatTy() || IsAtomic)
|
| + MaxAllowed = DL->getTypeAllocSize(Ty);
|
| + // If the alignment is set to 0, this means "use the default
|
| + // alignment for the target", which we fill in explicitly.
|
| + if (Alignment == 0 || Alignment >= MaxAllowed)
|
| + return MaxAllowed;
|
| + return 1;
|
| +}
|
| +
|
| +void stripFunctionAttrs(DataLayout *DL, Function *Func) {
|
| + CheckAttributes(Func->getAttributes());
|
| + Func->setAttributes(AttributeSet());
|
| + Func->setCallingConv(CallingConv::C);
|
| + Func->setAlignment(0);
|
| +
|
| + for (Function::iterator BB = Func->begin(), E = Func->end();
|
| + BB != E; ++BB) {
|
| + for (BasicBlock::iterator Inst = BB->begin(), E = BB->end();
|
| + Inst != E; ++Inst) {
|
| + CallSite Call(Inst);
|
| + if (Call) {
|
| + CheckAttributes(Call.getAttributes());
|
| + Call.setAttributes(AttributeSet());
|
| + Call.setCallingConv(CallingConv::C);
|
| +
|
| + // Set memcpy(), memmove() and memset() to use pessimistic
|
| + // alignment assumptions.
|
| + if (MemIntrinsic *MemOp = dyn_cast<MemIntrinsic>(Inst)) {
|
| + Type *AlignTy = MemOp->getAlignmentCst()->getType();
|
| + MemOp->setAlignment(ConstantInt::get(AlignTy, 1));
|
| + }
|
| + } else if (OverflowingBinaryOperator *Op =
|
| + dyn_cast<OverflowingBinaryOperator>(Inst)) {
|
| + cast<BinaryOperator>(Op)->setHasNoUnsignedWrap(false);
|
| + cast<BinaryOperator>(Op)->setHasNoSignedWrap(false);
|
| + } else if (PossiblyExactOperator *Op =
|
| + dyn_cast<PossiblyExactOperator>(Inst)) {
|
| + cast<BinaryOperator>(Op)->setIsExact(false);
|
| + } else if (LoadInst *Load = dyn_cast<LoadInst>(Inst)) {
|
| + Load->setAlignment(normalizeAlignment(
|
| + DL, Load->getAlignment(),
|
| + Load->getType(),
|
| + Load->isAtomic()));
|
| + } else if (StoreInst *Store = dyn_cast<StoreInst>(Inst)) {
|
| + Store->setAlignment(normalizeAlignment(
|
| + DL, Store->getAlignment(),
|
| + Store->getValueOperand()->getType(),
|
| + Store->isAtomic()));
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +bool StripAttributes::runOnModule(Module &M) {
|
| + DataLayout DL(&M);
|
| + for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func) {
|
| + // 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 (!Func->isIntrinsic()) {
|
| + stripGlobalValueAttrs(Func);
|
| + stripFunctionAttrs(&DL, Func);
|
| + }
|
| + }
|
| + for (Module::global_iterator GV = M.global_begin(), E = M.global_end();
|
| + GV != E; ++GV) {
|
| + stripGlobalValueAttrs(GV);
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +ModulePass *llvm::createStripAttributesPass() {
|
| + return new StripAttributes();
|
| +}
|
|
|