1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 10:32:48 +02:00

Place the BlockAddress type in the address space of the containing function

While this should not matter for most architectures (where the program
address space is 0), it is important for CHERI (and therefore Arm Morello).
We use address space 200 for all of our code pointers and without this
change we assert in the SelectionDAG handling of BlockAddress nodes.

It is also useful for AVR: previously programs targeting
AVR that attempt to read their own machine code
via a pointer to a label would instead read from RAM
using a pointer relative to the the start of program flash.

Reviewed By: dylanmckay, theraven
Differential Revision: https://reviews.llvm.org/D48803
This commit is contained in:
Alex Richardson 2021-07-02 11:25:43 +01:00
parent 6aab8b237f
commit a73a5b4199
7 changed files with 392 additions and 25 deletions

View File

@ -4078,7 +4078,11 @@ Addresses of Basic Blocks
``blockaddress(@function, %block)``
The '``blockaddress``' constant computes the address of the specified
basic block in the specified function, and always has an ``i8*`` type.
basic block in the specified function.
It always has an ``i8 addrspace(P)*`` type, where ``P`` is the address space
of the function containing ``%block`` (usually ``addrspace(0)``).
Taking the address of the entry block is illegal.
This value only has defined behavior when used as an operand to the

View File

@ -507,7 +507,8 @@ namespace llvm {
PerFunctionState &PFS);
// Constant Parsing.
bool parseValID(ValID &ID, PerFunctionState *PFS = nullptr);
bool parseValID(ValID &ID, PerFunctionState *PFS,
Type *ExpectedTy = nullptr);
bool parseGlobalValue(Type *Ty, Constant *&C);
bool parseGlobalTypeAndValue(Constant *&V);
bool parseGlobalValueVector(SmallVectorImpl<Constant *> &Elts,

View File

@ -970,7 +970,7 @@ bool LLParser::parseIndirectSymbol(const std::string &Name, LocTy NameLoc,
} else {
// The bitcast dest type is not present, it is implied by the dest type.
ValID ID;
if (parseValID(ID))
if (parseValID(ID, /*PFS=*/nullptr))
return true;
if (ID.Kind != ValID::t_Constant)
return error(AliaseeLoc, "invalid aliasee");
@ -3321,7 +3321,7 @@ BasicBlock *LLParser::PerFunctionState::defineBB(const std::string &Name,
/// sanity. PFS is used to convert function-local operands of metadata (since
/// metadata operands are not just parsed here but also converted to values).
/// PFS can be null when we are not parsing metadata values inside a function.
bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS) {
bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS, Type *ExpectedTy) {
ID.Loc = Lex.getLoc();
switch (Lex.getKind()) {
default:
@ -3493,10 +3493,10 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS) {
ValID Fn, Label;
if (parseToken(lltok::lparen, "expected '(' in block address expression") ||
parseValID(Fn) ||
parseValID(Fn, PFS) ||
parseToken(lltok::comma,
"expected comma in block address expression") ||
parseValID(Label) ||
parseValID(Label, PFS) ||
parseToken(lltok::rparen, "expected ')' in block address expression"))
return true;
@ -3531,9 +3531,27 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS) {
std::map<ValID, GlobalValue *>()))
.first->second.insert(std::make_pair(std::move(Label), nullptr))
.first->second;
if (!FwdRef)
FwdRef = new GlobalVariable(*M, Type::getInt8Ty(Context), false,
GlobalValue::InternalLinkage, nullptr, "");
if (!FwdRef) {
unsigned FwdDeclAS;
if (ExpectedTy) {
// If we know the type that the blockaddress is being assigned to,
// we can use the address space of that type.
if (!ExpectedTy->isPointerTy())
return error(ID.Loc,
"type of blockaddress must be a pointer and not '" +
getTypeString(ExpectedTy) + "'");
FwdDeclAS = ExpectedTy->getPointerAddressSpace();
} else if (PFS) {
// Otherwise, we default the address space of the current function.
FwdDeclAS = PFS->getFunction().getAddressSpace();
} else {
llvm_unreachable("Unknown address space for blockaddress");
}
FwdRef = new GlobalVariable(
*M, Type::getInt8Ty(Context), false, GlobalValue::InternalLinkage,
nullptr, "", nullptr, GlobalValue::NotThreadLocal, FwdDeclAS);
}
ID.ConstantVal = FwdRef;
ID.Kind = ValID::t_Constant;
return false;
@ -3570,7 +3588,7 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS) {
ValID Fn;
if (parseValID(Fn))
if (parseValID(Fn, PFS))
return true;
if (Fn.Kind != ValID::t_GlobalID && Fn.Kind != ValID::t_GlobalName)
@ -3960,7 +3978,7 @@ bool LLParser::parseGlobalValue(Type *Ty, Constant *&C) {
C = nullptr;
ValID ID;
Value *V = nullptr;
bool Parsed = parseValID(ID) ||
bool Parsed = parseValID(ID, /*PFS=*/nullptr, Ty) ||
convertValIDToValue(Ty, ID, V, nullptr, /*IsCall=*/false);
if (V && !(C = dyn_cast<Constant>(V)))
return error(ID.Loc, "global values must be constants");
@ -5679,7 +5697,9 @@ bool LLParser::convertValIDToValue(Type *Ty, ValID &ID, Value *&V,
return false;
case ValID::t_Constant:
if (ID.ConstantVal->getType() != Ty)
return error(ID.Loc, "constant expression type mismatch");
return error(ID.Loc, "constant expression type mismatch: got type '" +
getTypeString(ID.ConstantVal->getType()) +
"' but expected '" + getTypeString(Ty) + "'");
V = ID.ConstantVal;
return false;
case ValID::t_ConstantStruct:
@ -5739,7 +5759,7 @@ bool LLParser::parseConstantValue(Type *Ty, Constant *&C) {
bool LLParser::parseValue(Type *Ty, Value *&V, PerFunctionState *PFS) {
V = nullptr;
ValID ID;
return parseValID(ID, PFS) ||
return parseValID(ID, PFS, Ty) ||
convertValIDToValue(Ty, ID, V, PFS, /*IsCall=*/false);
}
@ -6033,7 +6053,12 @@ bool LLParser::PerFunctionState::resolveForwardRefBlockAddresses() {
if (!BB)
return P.error(BBID.Loc, "referenced value is not a basic block");
GV->replaceAllUsesWith(BlockAddress::get(&F, BB));
Value *ResolvedVal = BlockAddress::get(&F, BB);
ResolvedVal = P.checkValidVariableType(BBID.Loc, BBID.StrVal, GV->getType(),
ResolvedVal, false);
if (!ResolvedVal)
return true;
GV->replaceAllUsesWith(ResolvedVal);
GV->eraseFromParent();
}
@ -6568,7 +6593,7 @@ bool LLParser::parseInvoke(Instruction *&Inst, PerFunctionState &PFS) {
if (parseOptionalCallingConv(CC) || parseOptionalReturnAttrs(RetAttrs) ||
parseOptionalProgramAddrSpace(InvokeAddrSpace) ||
parseType(RetType, RetTypeLoc, true /*void allowed*/) ||
parseValID(CalleeID) || parseParameterList(ArgList, PFS) ||
parseValID(CalleeID, &PFS) || parseParameterList(ArgList, PFS) ||
parseFnAttributeValuePairs(FnAttrs, FwdRefAttrGrps, false,
NoBuiltinLoc) ||
parseOptionalOperandBundles(BundleList, PFS) ||
@ -6876,7 +6901,7 @@ bool LLParser::parseCallBr(Instruction *&Inst, PerFunctionState &PFS) {
BasicBlock *DefaultDest;
if (parseOptionalCallingConv(CC) || parseOptionalReturnAttrs(RetAttrs) ||
parseType(RetType, RetTypeLoc, true /*void allowed*/) ||
parseValID(CalleeID) || parseParameterList(ArgList, PFS) ||
parseValID(CalleeID, &PFS) || parseParameterList(ArgList, PFS) ||
parseFnAttributeValuePairs(FnAttrs, FwdRefAttrGrps, false,
NoBuiltinLoc) ||
parseOptionalOperandBundles(BundleList, PFS) ||
@ -7303,7 +7328,7 @@ bool LLParser::parseCall(Instruction *&Inst, PerFunctionState &PFS,
if (parseOptionalCallingConv(CC) || parseOptionalReturnAttrs(RetAttrs) ||
parseOptionalProgramAddrSpace(CallAddrSpace) ||
parseType(RetType, RetTypeLoc, true /*void allowed*/) ||
parseValID(CalleeID) ||
parseValID(CalleeID, &PFS) ||
parseParameterList(ArgList, PFS, TCK == CallInst::TCK_MustTail,
PFS.getFunction().isVarArg()) ||
parseFnAttributeValuePairs(FnAttrs, FwdRefAttrGrps, false, BuiltinLoc) ||
@ -7979,9 +8004,9 @@ bool LLParser::parseUseListOrderBB() {
ValID Fn, Label;
SmallVector<unsigned, 16> Indexes;
if (parseValID(Fn) ||
if (parseValID(Fn, /*PFS=*/nullptr) ||
parseToken(lltok::comma, "expected comma in uselistorder_bb directive") ||
parseValID(Label) ||
parseValID(Label, /*PFS=*/nullptr) ||
parseToken(lltok::comma, "expected comma in uselistorder_bb directive") ||
parseUseListOrderIndexes(Indexes))
return true;

View File

@ -1829,8 +1829,8 @@ BlockAddress *BlockAddress::get(Function *F, BasicBlock *BB) {
}
BlockAddress::BlockAddress(Function *F, BasicBlock *BB)
: Constant(Type::getInt8PtrTy(F->getContext()), Value::BlockAddressVal,
&Op<0>(), 2) {
: Constant(Type::getInt8PtrTy(F->getContext(), F->getAddressSpace()),
Value::BlockAddressVal, &Op<0>(), 2) {
setOperand(0, F);
setOperand(1, BB);
BB->AdjustBlockAddressRefCount(1);

View File

@ -0,0 +1,286 @@
; RUN: rm -rf %t && split-file %s %t
; RUN: llvm-as %t/global-use-good.ll -o - | llvm-dis -o /dev/null
; RUN: not llvm-as %t/global-use-bad.ll -o /dev/null 2>&1 | FileCheck %t/global-use-bad.ll
; RUN: llvm-as %t/global-fwddecl-good.ll -o - | llvm-dis -o /dev/null
; RUN: not llvm-as %t/global-fwddecl-bad.ll -o /dev/null 2>&1 | FileCheck %t/global-fwddecl-bad.ll
; RUN: llvm-as %t/return-fwddecl-good.ll -o - | llvm-dis -o /dev/null
; RUN: not llvm-as %t/return-fwddecl-bad.ll -o /dev/null 2>&1 | FileCheck %t/return-fwddecl-bad.ll
; RUN: llvm-as %t/return-self-good.ll -o - | llvm-dis -o /dev/null
; RUN: not llvm-as %t/return-self-bad.ll -o /dev/null 2>&1 | FileCheck %t/return-self-bad.ll
; RUN: not llvm-as %t/return-self-bad-2.ll -o /dev/null 2>&1 | FileCheck %t/return-self-bad-2.ll
; RUN: not llvm-as %t/return-unknown-fn-bad.ll -o /dev/null 2>&1 | FileCheck %t/return-unknown-fn-bad.ll
; RUN: llvm-as %t/call-fwddecl-good.ll -o - | llvm-dis -o /dev/null
; RUN: not llvm-as %t/call-fwddecl-bad.ll -o /dev/null 2>&1 | FileCheck %t/call-fwddecl-bad.ll
; RUN: llvm-as %t/phi-good.ll -o - | llvm-dis -o /dev/null
; RUN: not llvm-as %t/phi-bad.ll -o /dev/null 2>&1 | FileCheck %t/phi-bad.ll
; RUN: llvm-as %t/fwddecl-phi-good.ll -o - | llvm-dis -o /dev/null
; RUN: not llvm-as %t/fwddecl-phi-bad.ll -o /dev/null 2>&1 | FileCheck %t/fwddecl-phi-bad.ll
; RUN: not llvm-as %t/bad-type-not-ptr.ll -o /dev/null 2>&1 | FileCheck %t/bad-type-not-ptr.ll
; RUN: not llvm-as %t/bad-type-not-i8-ptr.ll -o /dev/null 2>&1 | FileCheck %t/bad-type-not-i8-ptr.ll
;--- global-use-good.ll
target datalayout = "P2"
define void @fn_in_prog_as_implicit() {
unreachable
bb:
ret void
}
define void @fn_in_prog_as_explicit() addrspace(2) {
unreachable
bb:
ret void
}
define void @fn_in_other_as() addrspace(1) {
unreachable
bb:
ret void
}
@global1 = constant i8 addrspace(2)* blockaddress(@fn_in_prog_as_implicit, %bb)
@global2 = constant i8 addrspace(2)* blockaddress(@fn_in_prog_as_explicit, %bb)
@global3 = constant i8 addrspace(1)* blockaddress(@fn_in_other_as, %bb)
;--- global-use-bad.ll
define void @fn() addrspace(1) {
unreachable
bb:
ret void
}
@global1 = constant i8 addrspace(2)* blockaddress(@fn, %bb)
; CHECK: [[#@LINE-1]]:38: error: constant expression type mismatch: got type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
; Check that a global blockaddress of a forward-declared function
; uses the type of the global variable address space for the forward declaration
;--- global-fwddecl-good.ll
@global = constant i8 addrspace(2)* blockaddress(@fwddecl_in_prog_as, %bb)
define void @fwddecl_in_prog_as() addrspace(2) {
unreachable
bb:
ret void
}
;--- global-fwddecl-bad.ll
; This forward declaration does not match the actual function type so we should get an error:
@global = constant i8 addrspace(2)* blockaddress(@fwddecl_in_unexpected_as, %bb)
; CHECK: [[#@LINE-1]]:77: error: 'bb' defined with type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
define void @fwddecl_in_unexpected_as() addrspace(1) {
unreachable
bb:
ret void
}
; When returning blockaddresses of forward-declared functions we
; can also use the type of the variable.
;--- return-fwddecl-good.ll
define i8 addrspace(2)* @take_as2() {
ret i8 addrspace(2)* blockaddress(@fwddecl_as2, %bb)
}
define i8 addrspace(1)* @take_as1() {
ret i8 addrspace(1)* blockaddress(@fwddecl_as1, %bb)
}
define void @fwddecl_as1() addrspace(1) {
unreachable
bb:
ret void
}
define void @fwddecl_as2() addrspace(2) {
unreachable
bb:
ret void
}
;--- return-fwddecl-bad.ll
define i8 addrspace(2)* @take_bad() {
ret i8 addrspace(2)* blockaddress(@fwddecl_as1, %bb)
; CHECK: [[#@LINE-1]]:51: error: 'bb' defined with type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
}
define void @fwddecl_as1() addrspace(1) {
unreachable
bb:
ret void
}
;--- return-self-good.ll
target datalayout = "P2"
define i8 addrspace(0)* @take_self_as0() addrspace(0) {
L1:
br label %L2
L2:
ret i8 addrspace(0)* blockaddress(@take_self_as0, %L3)
L3:
unreachable
}
define i8 addrspace(2)* @take_self_prog_as() {
L1:
br label %L2
L2:
ret i8 addrspace(2)* blockaddress(@take_self_prog_as, %L3)
L3:
unreachable
}
define i8 addrspace(1)* @take_self_as1() addrspace(1) {
L1:
br label %L2
L2:
ret i8 addrspace(1)* blockaddress(@take_self_as1, %L3)
L3:
unreachable
}
define i8 addrspace(2)* @take_self_as2() addrspace(2) {
L1:
br label %L2
L2:
ret i8 addrspace(2)* blockaddress(@take_self_as2, %L3)
L3:
unreachable
}
;--- return-self-bad.ll
target datalayout = "P2"
define i8 addrspace(2)* @take_self_bad() addrspace(1) {
L1:
br label %L2
L2:
ret i8 addrspace(2)* blockaddress(@take_self_bad, %L3)
; CHECK: [[#@LINE-1]]:24: error: constant expression type mismatch: got type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
L3:
unreachable
}
;--- return-self-bad-2.ll
target datalayout = "P2"
define i8* @take_self_bad_prog_as() {
L1:
br label %L2
L2:
ret i8* blockaddress(@take_self_bad_prog_as, %L3)
; CHECK: [[#@LINE-1]]:11: error: constant expression type mismatch: got type 'i8 addrspace(2)*' but expected 'i8*'
L3:
unreachable
}
;--- return-unknown-fn-bad.ll
target datalayout = "P2"
define i8 addrspace(1)* @return_unknown_fn() addrspace(1) {
ret i8 addrspace(1)* blockaddress(@undefined, %bb)
; CHECK: [[#@LINE-1]]:37: error: expected function name in blockaddress
}
;--- call-fwddecl-good.ll
target datalayout = "P2"
define void @call_from_fn_in_as2() addrspace(2) {
call addrspace(2) void bitcast (i8 addrspace(2)* blockaddress(@fwddecl_as2, %bb) to void () addrspace(2)*)()
ret void
}
define void @call_from_fn_in_as1() addrspace(1) {
call addrspace(1) void bitcast (i8 addrspace(1)* blockaddress(@fwddecl_as1, %bb) to void () addrspace(1)*)()
ret void
}
define void @fwddecl_as2() addrspace(2) {
unreachable
bb:
ret void
}
define void @fwddecl_as1() addrspace(1) {
unreachable
bb:
ret void
}
;--- call-fwddecl-bad.ll
target datalayout = "P2"
define void @call_from_fn_in_as2_explicit() addrspace(2) {
call addrspace(2) void bitcast (i8 addrspace(2)* blockaddress(@fwddecl_as1, %bb) to void () addrspace(2)*)()
; CHECK: [[#@LINE-1]]:79: error: 'bb' defined with type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
ret void
}
define void @fwddecl_as1() addrspace(1) {
unreachable
bb:
ret void
}
;--- phi-good.ll
target datalayout = "P2"
define i8 addrspace(1)* @f1() addrspace(1) {
L1:
br label %L3
L2:
br label %L3
L3:
%p = phi i8 addrspace(1)* [ blockaddress(@f1, %L4), %L2 ], [ null, %L1 ]
ret i8 addrspace(1)* %p
L4:
unreachable
}
define i8 addrspace(2)* @f2() {
L1:
br label %L3
L2:
br label %L3
L3:
%p = phi i8 addrspace(2)* [ blockaddress(@f2, %L4), %L2 ], [ null, %L1 ]
ret i8 addrspace(2)* %p
L4:
unreachable
}
;--- phi-bad.ll
target datalayout = "P2"
define i8* @f() {
L1:
br label %L3
L2:
br label %L3
L3:
%p = phi i8* [ blockaddress(@f, %L4), %L2 ], [ null, %L1 ]
; CHECK: [[#@LINE-1]]:18: error: constant expression type mismatch: got type 'i8 addrspace(2)*' but expected 'i8*'
ret i8* %p
}
; A blockaddress function forward-declaration used in a phi node should
; create the forward declaration in the same address space as the current function
;--- fwddecl-phi-good.ll
define i8 addrspace(1)* @f() addrspace(1) {
L1:
br label %L3
L2:
br label %L3
L3:
%p = phi i8 addrspace(1)* [ blockaddress(@fwddecl_as1, %bb), %L2 ], [ null, %L1 ]
ret i8 addrspace(1)* %p
L4:
unreachable
}
define void @fwddecl_as1() addrspace(1) {
unreachable
bb:
ret void
}
;--- fwddecl-phi-bad.ll
define i8 addrspace(2)* @f() addrspace(2) {
L1:
br label %L3
L2:
br label %L3
L3:
%p = phi i8 addrspace(2)* [ blockaddress(@fwddecl_as1, %bb), %L2 ], [ null, %L1 ]
; CHECK: [[#@LINE-1]]:58: error: 'bb' defined with type 'i8 addrspace(1)*' but expected 'i8 addrspace(2)*'
ret i8 addrspace(2)* %p
L4:
unreachable
}
define void @fwddecl_as1() addrspace(1) {
unreachable
bb:
ret void
}
;--- bad-type-not-ptr.ll
@global = constant i8 blockaddress(@unknown_fn, %bb)
; CHECK: [[#@LINE-1]]:23: error: type of blockaddress must be a pointer and not 'i8'
;--- bad-type-not-i8-ptr.ll
@global = constant i32* blockaddress(@unknown_fn, %bb)
; CHECK: [[#@LINE-1]]:25: error: constant expression type mismatch: got type 'i8*' but expected 'i32*'

View File

@ -0,0 +1,51 @@
; RUN: llc -mcpu=atmega328 < %s -march=avr | FileCheck %s
; This test verifies that the pointer to a basic block
; should always be a pointer in address space 1.
;
; If this were not the case, then programs targeting
; AVR that attempted to read their own machine code
; via a pointer to a label would actually read from RAM
; using a pointer relative to the the start of program flash.
;
; This would cause a load of uninitialized memory, not even
; touching the program's machine code as otherwise desired.
target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
; CHECK-LABEL: load_with_no_forward_reference
define i8 @load_with_no_forward_reference(i8 %a, i8 %b) {
second:
; CHECK: ldi r30, .Ltmp0+2
; CHECK-NEXT: ldi r31, .Ltmp0+4
; CHECK: lpm r24, Z
%bar = load i8, i8 addrspace(1)* blockaddress(@function_with_no_forward_reference, %second)
ret i8 %bar
}
; CHECK-LABEL: load_from_local_label
define i8 @load_from_local_label(i8 %a, i8 %b) {
entry:
%result1 = add i8 %a, %b
br label %second
; CHECK-LABEL: .Ltmp1:
second:
; CHECK: ldi r30, .Ltmp1+2
; CHECK-NEXT: ldi r31, .Ltmp1+4
; CHECK-NEXT: lpm r24, Z
%result2 = load i8, i8 addrspace(1)* blockaddress(@load_from_local_label, %second)
ret i8 %result2
}
; A function with no forward reference, right at the end
; of the file.
define i8 @function_with_no_forward_reference(i8 %a, i8 %b) {
entry:
%result = add i8 %a, %b
br label %second
second:
ret i8 0
}

View File

@ -1,15 +1,15 @@
; RUN: llc -mattr=sram,eijmpcall < %s -march=avr -verify-machineinstrs | FileCheck %s
@brind.k = private unnamed_addr constant [2 x i8*] [i8* blockaddress(@brind, %return), i8* blockaddress(@brind, %b)], align 1
@brind.k = private unnamed_addr constant [2 x i8 addrspace(1)*] [i8 addrspace(1)* blockaddress(@brind, %return), i8 addrspace(1)* blockaddress(@brind, %b)], align 1
define i8 @brind(i8 %p) {
; CHECK-LABEL: brind:
; CHECK: ijmp
entry:
%idxprom = sext i8 %p to i16
%arrayidx = getelementptr inbounds [2 x i8*], [2 x i8*]* @brind.k, i16 0, i16 %idxprom
%s = load i8*, i8** %arrayidx
indirectbr i8* %s, [label %return, label %b]
%arrayidx = getelementptr inbounds [2 x i8 addrspace(1)*], [2 x i8 addrspace(1)*]* @brind.k, i16 0, i16 %idxprom
%s = load i8 addrspace(1)*, i8 addrspace(1)** %arrayidx
indirectbr i8 addrspace(1)* %s, [label %return, label %b]
b:
br label %return
return: