mirror of
https://github.com/RPCS3/llvm.git
synced 2026-07-01 21:04:04 -04:00
[IR][ARM] Add function pointer alignment to datalayout
Use this feature to fix a bug on ARM where 4 byte alignment is incorrectly assumed. Differential Revision: https://reviews.llvm.org/D57335 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@355685 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -2004,6 +2004,14 @@ as follows:
|
||||
targets.
|
||||
``a:<abi>:<pref>``
|
||||
This specifies the alignment for an object of aggregate type.
|
||||
``F<type><abi>``
|
||||
This specifies the alignment for function pointers.
|
||||
The options for ``<type>`` are:
|
||||
|
||||
* ``i``: The alignment of function pointers is independent of the alignment
|
||||
of functions, and is a multiple of ``<abi>``.
|
||||
* ``n``: The alignment of function pointers is a multiple of the explicit
|
||||
alignment specified on the function, and is a multiple of ``<abi>``.
|
||||
``m:<mangling>``
|
||||
If present, specifies that llvm names are mangled in the output. Symbols
|
||||
prefixed with the mangling escape character ``\01`` are passed through
|
||||
|
||||
@@ -108,6 +108,13 @@ struct PointerAlignElem {
|
||||
/// generating LLVM IR is required to generate the right target data for the
|
||||
/// target being codegen'd to.
|
||||
class DataLayout {
|
||||
public:
|
||||
enum class FunctionPtrAlignType {
|
||||
/// The function pointer alignment is independent of the function alignment.
|
||||
Independent,
|
||||
/// The function pointer alignment is a multiple of the function alignment.
|
||||
MultipleOfFunctionAlign,
|
||||
};
|
||||
private:
|
||||
/// Defaults to false.
|
||||
bool BigEndian;
|
||||
@@ -116,6 +123,9 @@ private:
|
||||
unsigned StackNaturalAlign;
|
||||
unsigned ProgramAddrSpace;
|
||||
|
||||
unsigned FunctionPtrAlign;
|
||||
FunctionPtrAlignType TheFunctionPtrAlignType;
|
||||
|
||||
enum ManglingModeT {
|
||||
MM_None,
|
||||
MM_ELF,
|
||||
@@ -199,6 +209,8 @@ public:
|
||||
BigEndian = DL.isBigEndian();
|
||||
AllocaAddrSpace = DL.AllocaAddrSpace;
|
||||
StackNaturalAlign = DL.StackNaturalAlign;
|
||||
FunctionPtrAlign = DL.FunctionPtrAlign;
|
||||
TheFunctionPtrAlignType = DL.TheFunctionPtrAlignType;
|
||||
ProgramAddrSpace = DL.ProgramAddrSpace;
|
||||
ManglingMode = DL.ManglingMode;
|
||||
LegalIntWidths = DL.LegalIntWidths;
|
||||
@@ -256,6 +268,17 @@ public:
|
||||
unsigned getStackAlignment() const { return StackNaturalAlign; }
|
||||
unsigned getAllocaAddrSpace() const { return AllocaAddrSpace; }
|
||||
|
||||
/// Returns the alignment of function pointers, which may or may not be
|
||||
/// related to the alignment of functions.
|
||||
/// \see getFunctionPtrAlignType
|
||||
unsigned getFunctionPtrAlign() const { return FunctionPtrAlign; }
|
||||
|
||||
/// Return the type of function pointer alignment.
|
||||
/// \see getFunctionPtrAlign
|
||||
FunctionPtrAlignType getFunctionPtrAlignType() const {
|
||||
return TheFunctionPtrAlignType;
|
||||
}
|
||||
|
||||
unsigned getProgramAddressSpace() const { return ProgramAddrSpace; }
|
||||
|
||||
bool hasMicrosoftFastStdCallMangling() const {
|
||||
|
||||
+24
-4
@@ -26,6 +26,7 @@
|
||||
#include "llvm/IR/GlobalAlias.h"
|
||||
#include "llvm/IR/GlobalVariable.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Operator.h"
|
||||
#include "llvm/IR/PatternMatch.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
@@ -1076,10 +1077,29 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode, Constant *C1,
|
||||
isa<GlobalValue>(CE1->getOperand(0))) {
|
||||
GlobalValue *GV = cast<GlobalValue>(CE1->getOperand(0));
|
||||
|
||||
// Functions are at least 4-byte aligned.
|
||||
unsigned GVAlign = GV->getAlignment();
|
||||
if (isa<Function>(GV))
|
||||
GVAlign = std::max(GVAlign, 4U);
|
||||
unsigned GVAlign;
|
||||
|
||||
if (Module *TheModule = GV->getParent()) {
|
||||
GVAlign = GV->getPointerAlignment(TheModule->getDataLayout());
|
||||
|
||||
// If the function alignment is not specified then assume that it
|
||||
// is 4.
|
||||
// This is dangerous; on x86, the alignment of the pointer
|
||||
// corresponds to the alignment of the function, but might be less
|
||||
// than 4 if it isn't explicitly specified.
|
||||
// However, a fix for this behaviour was reverted because it
|
||||
// increased code size (see https://reviews.llvm.org/D55115)
|
||||
// FIXME: This code should be deleted once existing targets have
|
||||
// appropriate defaults
|
||||
if (GVAlign == 0U && isa<Function>(GV))
|
||||
GVAlign = 4U;
|
||||
} else if (isa<Function>(GV)) {
|
||||
// Without a datalayout we have to assume the worst case: that the
|
||||
// function pointer isn't aligned at all.
|
||||
GVAlign = 0U;
|
||||
} else {
|
||||
GVAlign = GV->getAlignment();
|
||||
}
|
||||
|
||||
if (GVAlign > 1) {
|
||||
unsigned DstWidth = CI2->getType()->getBitWidth();
|
||||
|
||||
@@ -184,6 +184,8 @@ void DataLayout::reset(StringRef Desc) {
|
||||
AllocaAddrSpace = 0;
|
||||
StackNaturalAlign = 0;
|
||||
ProgramAddrSpace = 0;
|
||||
FunctionPtrAlign = 0;
|
||||
TheFunctionPtrAlignType = FunctionPtrAlignType::Independent;
|
||||
ManglingMode = MM_None;
|
||||
NonIntegralAddressSpaces.clear();
|
||||
|
||||
@@ -379,6 +381,22 @@ void DataLayout::parseSpecifier(StringRef Desc) {
|
||||
StackNaturalAlign = inBytes(getInt(Tok));
|
||||
break;
|
||||
}
|
||||
case 'F': {
|
||||
switch (Tok.front()) {
|
||||
case 'i':
|
||||
TheFunctionPtrAlignType = FunctionPtrAlignType::Independent;
|
||||
break;
|
||||
case 'n':
|
||||
TheFunctionPtrAlignType = FunctionPtrAlignType::MultipleOfFunctionAlign;
|
||||
break;
|
||||
default:
|
||||
report_fatal_error("Unknown function pointer alignment type in "
|
||||
"datalayout string");
|
||||
}
|
||||
Tok = Tok.substr(1);
|
||||
FunctionPtrAlign = inBytes(getInt(Tok));
|
||||
break;
|
||||
}
|
||||
case 'P': { // Function address space.
|
||||
ProgramAddrSpace = getAddrSpace(Tok);
|
||||
break;
|
||||
@@ -432,6 +450,8 @@ bool DataLayout::operator==(const DataLayout &Other) const {
|
||||
AllocaAddrSpace == Other.AllocaAddrSpace &&
|
||||
StackNaturalAlign == Other.StackNaturalAlign &&
|
||||
ProgramAddrSpace == Other.ProgramAddrSpace &&
|
||||
FunctionPtrAlign == Other.FunctionPtrAlign &&
|
||||
TheFunctionPtrAlignType == Other.TheFunctionPtrAlignType &&
|
||||
ManglingMode == Other.ManglingMode &&
|
||||
LegalIntWidths == Other.LegalIntWidths &&
|
||||
Alignments == Other.Alignments && Pointers == Other.Pointers;
|
||||
|
||||
+8
-4
@@ -648,10 +648,14 @@ unsigned Value::getPointerAlignment(const DataLayout &DL) const {
|
||||
|
||||
unsigned Align = 0;
|
||||
if (auto *GO = dyn_cast<GlobalObject>(this)) {
|
||||
// Don't make any assumptions about function pointer alignment. Some
|
||||
// targets use the LSBs to store additional information.
|
||||
if (isa<Function>(GO))
|
||||
return 0;
|
||||
if (isa<Function>(GO)) {
|
||||
switch (DL.getFunctionPtrAlignType()) {
|
||||
case DataLayout::FunctionPtrAlignType::Independent:
|
||||
return DL.getFunctionPtrAlign();
|
||||
case DataLayout::FunctionPtrAlignType::MultipleOfFunctionAlign:
|
||||
return std::max(DL.getFunctionPtrAlign(), GO->getAlignment());
|
||||
}
|
||||
}
|
||||
Align = GO->getAlignment();
|
||||
if (Align == 0) {
|
||||
if (auto *GVar = dyn_cast<GlobalVariable>(GO)) {
|
||||
|
||||
@@ -141,6 +141,10 @@ static std::string computeDataLayout(const Triple &TT, StringRef CPU,
|
||||
// Pointers are 32 bits and aligned to 32 bits.
|
||||
Ret += "-p:32:32";
|
||||
|
||||
// Function pointers are aligned to 8 bits (because the LSB stores the
|
||||
// ARM/Thumb state).
|
||||
Ret += "-Fi8";
|
||||
|
||||
// ABIs other than APCS have 64 bit integers with natural alignment.
|
||||
if (ABI != ARMBaseTargetMachine::ARM_ABI_APCS)
|
||||
Ret += "-i64:64";
|
||||
|
||||
@@ -13,6 +13,7 @@ add_llvm_unittest(IRTests
|
||||
CFGBuilder.cpp
|
||||
ConstantRangeTest.cpp
|
||||
ConstantsTest.cpp
|
||||
DataLayoutTest.cpp
|
||||
DebugInfoTest.cpp
|
||||
DebugTypeODRUniquingTest.cpp
|
||||
DominatorTreeTest.cpp
|
||||
|
||||
@@ -475,5 +475,113 @@ TEST(ConstantsTest, BitcastToGEP) {
|
||||
ASSERT_EQ(cast<ConstantExpr>(C)->getOpcode(), Instruction::BitCast);
|
||||
}
|
||||
|
||||
bool foldFuncPtrAndConstToNull(LLVMContext &Context, Module *TheModule,
|
||||
uint64_t AndValue, unsigned FunctionAlign = 0) {
|
||||
Type *VoidType(Type::getVoidTy(Context));
|
||||
FunctionType *FuncType(FunctionType::get(VoidType, false));
|
||||
Function *Func(Function::Create(
|
||||
FuncType, GlobalValue::ExternalLinkage, "", TheModule));
|
||||
|
||||
if (FunctionAlign) Func->setAlignment(FunctionAlign);
|
||||
|
||||
IntegerType *ConstantIntType(Type::getInt32Ty(Context));
|
||||
ConstantInt *TheConstant(ConstantInt::get(ConstantIntType, AndValue));
|
||||
|
||||
Constant *TheConstantExpr(
|
||||
ConstantExpr::getPtrToInt(Func, ConstantIntType));
|
||||
|
||||
|
||||
bool result = ConstantExpr::get(Instruction::And, TheConstantExpr,
|
||||
TheConstant)->isNullValue();
|
||||
|
||||
if (!TheModule) {
|
||||
// If the Module exists then it will delete the Function.
|
||||
delete Func;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST(ConstantsTest, FoldFunctionPtrAlignUnknownAnd2) {
|
||||
LLVMContext Context;
|
||||
Module TheModule("TestModule", Context);
|
||||
// When the DataLayout doesn't specify a function pointer alignment we
|
||||
// assume in this case that it is 4 byte aligned. This is a bug but we can't
|
||||
// fix it directly because it causes a code size regression on X86.
|
||||
// FIXME: This test should be changed once existing targets have
|
||||
// appropriate defaults. See associated FIXME in ConstantFoldBinaryInstruction
|
||||
ASSERT_TRUE(foldFuncPtrAndConstToNull(Context, &TheModule, 2));
|
||||
}
|
||||
|
||||
TEST(ConstantsTest, DontFoldFunctionPtrAlignUnknownAnd4) {
|
||||
LLVMContext Context;
|
||||
Module TheModule("TestModule", Context);
|
||||
ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, &TheModule, 4));
|
||||
}
|
||||
|
||||
TEST(ConstantsTest, FoldFunctionPtrAlign4) {
|
||||
LLVMContext Context;
|
||||
Module TheModule("TestModule", Context);
|
||||
const char* AlignmentStrings[] = { "Fi32", "Fn32" };
|
||||
|
||||
for (unsigned AndValue = 1; AndValue <= 2; ++AndValue) {
|
||||
for (const char *AlignmentString : AlignmentStrings) {
|
||||
TheModule.setDataLayout(AlignmentString);
|
||||
ASSERT_TRUE(foldFuncPtrAndConstToNull(Context, &TheModule, AndValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ConstantsTest, DontFoldFunctionPtrAlign1) {
|
||||
LLVMContext Context;
|
||||
Module TheModule("TestModule", Context);
|
||||
const char* AlignmentStrings[] = { "Fi8", "Fn8" };
|
||||
|
||||
for (const char* AlignmentString : AlignmentStrings) {
|
||||
TheModule.setDataLayout(AlignmentString);
|
||||
ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, &TheModule, 2));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ConstantsTest, FoldFunctionAlign4PtrAlignMultiple) {
|
||||
LLVMContext Context;
|
||||
Module TheModule("TestModule", Context);
|
||||
TheModule.setDataLayout("Fn8");
|
||||
ASSERT_TRUE(foldFuncPtrAndConstToNull(Context, &TheModule, 2, 4));
|
||||
}
|
||||
|
||||
TEST(ConstantsTest, DontFoldFunctionAlign4PtrAlignIndependent) {
|
||||
LLVMContext Context;
|
||||
Module TheModule("TestModule", Context);
|
||||
TheModule.setDataLayout("Fi8");
|
||||
ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, &TheModule, 2, 4));
|
||||
}
|
||||
|
||||
TEST(ConstantsTest, DontFoldFunctionPtrIfNoModule) {
|
||||
LLVMContext Context;
|
||||
// Even though the function is explicitly 4 byte aligned, in the absence of a
|
||||
// DataLayout we can't assume that the function pointer is aligned.
|
||||
ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, nullptr, 2, 4));
|
||||
}
|
||||
|
||||
TEST(ConstantsTest, FoldGlobalVariablePtr) {
|
||||
LLVMContext Context;
|
||||
|
||||
IntegerType *IntType(Type::getInt32Ty(Context));
|
||||
|
||||
std::unique_ptr<GlobalVariable> Global(
|
||||
new GlobalVariable(IntType, true, GlobalValue::ExternalLinkage));
|
||||
|
||||
Global->setAlignment(4);
|
||||
|
||||
ConstantInt *TheConstant(ConstantInt::get(IntType, 2));
|
||||
|
||||
Constant *TheConstantExpr(
|
||||
ConstantExpr::getPtrToInt(Global.get(), IntType));
|
||||
|
||||
ASSERT_TRUE(ConstantExpr::get( \
|
||||
Instruction::And, TheConstantExpr, TheConstant)->isNullValue());
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
} // end namespace llvm
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
//===- ConstantRangeTest.cpp - ConstantRange tests ------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(DataLayoutTest, FunctionPtrAlign) {
|
||||
EXPECT_EQ(0U, DataLayout("").getFunctionPtrAlign());
|
||||
EXPECT_EQ(1U, DataLayout("Fi8").getFunctionPtrAlign());
|
||||
EXPECT_EQ(2U, DataLayout("Fi16").getFunctionPtrAlign());
|
||||
EXPECT_EQ(4U, DataLayout("Fi32").getFunctionPtrAlign());
|
||||
EXPECT_EQ(8U, DataLayout("Fi64").getFunctionPtrAlign());
|
||||
EXPECT_EQ(1U, DataLayout("Fn8").getFunctionPtrAlign());
|
||||
EXPECT_EQ(2U, DataLayout("Fn16").getFunctionPtrAlign());
|
||||
EXPECT_EQ(4U, DataLayout("Fn32").getFunctionPtrAlign());
|
||||
EXPECT_EQ(8U, DataLayout("Fn64").getFunctionPtrAlign());
|
||||
EXPECT_EQ(DataLayout::FunctionPtrAlignType::Independent, \
|
||||
DataLayout("").getFunctionPtrAlignType());
|
||||
EXPECT_EQ(DataLayout::FunctionPtrAlignType::Independent, \
|
||||
DataLayout("Fi8").getFunctionPtrAlignType());
|
||||
EXPECT_EQ(DataLayout::FunctionPtrAlignType::MultipleOfFunctionAlign, \
|
||||
DataLayout("Fn8").getFunctionPtrAlignType());
|
||||
EXPECT_EQ(DataLayout("Fi8"), DataLayout("Fi8"));
|
||||
EXPECT_NE(DataLayout("Fi8"), DataLayout("Fi16"));
|
||||
EXPECT_NE(DataLayout("Fi8"), DataLayout("Fn8"));
|
||||
|
||||
DataLayout a(""), b("Fi8"), c("Fn8");
|
||||
EXPECT_NE(a, b);
|
||||
EXPECT_NE(a, c);
|
||||
EXPECT_NE(b, c);
|
||||
|
||||
a = b;
|
||||
EXPECT_EQ(a, b);
|
||||
a = c;
|
||||
EXPECT_EQ(a, c);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
@@ -129,4 +129,29 @@ TEST(FunctionTest, setSection) {
|
||||
EXPECT_TRUE(F->hasSection());
|
||||
}
|
||||
|
||||
TEST(FunctionTest, GetPointerAlignment) {
|
||||
LLVMContext Context;
|
||||
Type *VoidType(Type::getVoidTy(Context));
|
||||
FunctionType *FuncType(FunctionType::get(VoidType, false));
|
||||
std::unique_ptr<Function> Func(Function::Create(
|
||||
FuncType, GlobalValue::ExternalLinkage));
|
||||
EXPECT_EQ(0U, Func->getPointerAlignment(DataLayout("")));
|
||||
EXPECT_EQ(1U, Func->getPointerAlignment(DataLayout("Fi8")));
|
||||
EXPECT_EQ(1U, Func->getPointerAlignment(DataLayout("Fn8")));
|
||||
EXPECT_EQ(2U, Func->getPointerAlignment(DataLayout("Fi16")));
|
||||
EXPECT_EQ(2U, Func->getPointerAlignment(DataLayout("Fn16")));
|
||||
EXPECT_EQ(4U, Func->getPointerAlignment(DataLayout("Fi32")));
|
||||
EXPECT_EQ(4U, Func->getPointerAlignment(DataLayout("Fn32")));
|
||||
|
||||
Func->setAlignment(4U);
|
||||
|
||||
EXPECT_EQ(0U, Func->getPointerAlignment(DataLayout("")));
|
||||
EXPECT_EQ(1U, Func->getPointerAlignment(DataLayout("Fi8")));
|
||||
EXPECT_EQ(4U, Func->getPointerAlignment(DataLayout("Fn8")));
|
||||
EXPECT_EQ(2U, Func->getPointerAlignment(DataLayout("Fi16")));
|
||||
EXPECT_EQ(4U, Func->getPointerAlignment(DataLayout("Fn16")));
|
||||
EXPECT_EQ(4U, Func->getPointerAlignment(DataLayout("Fi32")));
|
||||
EXPECT_EQ(4U, Func->getPointerAlignment(DataLayout("Fn32")));
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
Reference in New Issue
Block a user