35 changes: 35 additions & 0 deletions llvm/include/llvm/IR/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,41 @@ struct OperandTraits<DSOLocalEquivalent>

DEFINE_TRANSPARENT_OPERAND_ACCESSORS(DSOLocalEquivalent, Value)

/// Wrapper for a value that won't be replaced with a CFI jump table
/// pointer in LowerTypeTestsModule.
class NoCFIValue final : public Constant {
friend class Constant;

NoCFIValue(GlobalValue *GV);

void *operator new(size_t S) { return User::operator new(S, 1); }

void destroyConstantImpl();
Value *handleOperandChangeImpl(Value *From, Value *To);

public:
/// Return a NoCFIValue for the specified function.
static NoCFIValue *get(GlobalValue *GV);

/// Transparently provide more efficient getOperand methods.
DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);

GlobalValue *getGlobalValue() const {
return cast<GlobalValue>(Op<0>().get());
}

/// Methods for support type inquiry through isa, cast, and dyn_cast:
static bool classof(const Value *V) {
return V->getValueID() == NoCFIValueVal;
}
};

template <>
struct OperandTraits<NoCFIValue> : public FixedNumOperandTraits<NoCFIValue, 1> {
};

DEFINE_TRANSPARENT_OPERAND_ACCESSORS(NoCFIValue, Value)

//===----------------------------------------------------------------------===//
/// A constant value that is initialized with an expression using
/// other constant values.
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/Value.def
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ HANDLE_GLOBAL_VALUE(GlobalVariable)
HANDLE_CONSTANT(BlockAddress)
HANDLE_CONSTANT(ConstantExpr)
HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(DSOLocalEquivalent)
HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(NoCFIValue)

// ConstantAggregate.
HANDLE_CONSTANT(ConstantArray)
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(x);
KEYWORD(blockaddress);
KEYWORD(dso_local_equivalent);
KEYWORD(no_cfi);

// Metadata types.
KEYWORD(distinct);
Expand Down
18 changes: 18 additions & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3288,6 +3288,20 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS, Type *ExpectedTy) {
return false;
}

case lltok::kw_no_cfi: {
// ValID ::= 'no_cfi' @foo
Lex.Lex();

if (parseValID(ID, PFS))
return true;

if (ID.Kind != ValID::t_GlobalID && ID.Kind != ValID::t_GlobalName)
return error(ID.Loc, "expected global value name in no_cfi");

ID.NoCFI = true;
return false;
}

case lltok::kw_trunc:
case lltok::kw_zext:
case lltok::kw_sext:
Expand Down Expand Up @@ -5268,9 +5282,13 @@ bool LLParser::convertValIDToValue(Type *Ty, ValID &ID, Value *&V,
}
case ValID::t_GlobalName:
V = getGlobalVal(ID.StrVal, Ty, ID.Loc);
if (V && ID.NoCFI)
V = NoCFIValue::get(cast<GlobalValue>(V));
return V == nullptr;
case ValID::t_GlobalID:
V = getGlobalVal(ID.UIntVal, Ty, ID.Loc);
if (V && ID.NoCFI)
V = NoCFIValue::get(cast<GlobalValue>(V));
return V == nullptr;
case ValID::t_APSInt:
if (!Ty->isIntegerTy())
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ static Optional<const char *> GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(CST_CODE, CE_SHUFVEC_EX)
STRINGIFY_CODE(CST_CODE, CE_UNOP)
STRINGIFY_CODE(CST_CODE, DSO_LOCAL_EQUIVALENT)
STRINGIFY_CODE(CST_CODE, NO_CFI_VALUE)
case bitc::CST_CODE_BLOCKADDRESS:
return "CST_CODE_BLOCKADDRESS";
STRINGIFY_CODE(CST_CODE, DATA)
Expand Down
13 changes: 13 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2938,6 +2938,19 @@ Error BitcodeReader::parseConstants() {
V = DSOLocalEquivalent::get(GV);
break;
}
case bitc::CST_CODE_NO_CFI_VALUE: {
if (Record.size() < 2)
return error("Invalid record");
Type *GVTy = getTypeByID(Record[0]);
if (!GVTy)
return error("Invalid record");
GlobalValue *GV = dyn_cast_or_null<GlobalValue>(
ValueList.getConstantFwdRef(Record[1], GVTy));
if (!GV)
return error("Invalid record");
V = NoCFIValue::get(GV);
break;
}
}

ValueList.assignValue(V, NextCstNo);
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2657,6 +2657,10 @@ void ModuleBitcodeWriter::writeConstants(unsigned FirstVal, unsigned LastVal,
Code = bitc::CST_CODE_DSO_LOCAL_EQUIVALENT;
Record.push_back(VE.getTypeID(Equiv->getGlobalValue()->getType()));
Record.push_back(VE.getValueID(Equiv->getGlobalValue()));
} else if (const auto *NC = dyn_cast<NoCFIValue>(C)) {
Code = bitc::CST_CODE_NO_CFI_VALUE;
Record.push_back(VE.getTypeID(NC->getGlobalValue()->getType()));
Record.push_back(VE.getValueID(NC->getGlobalValue()));
} else {
#ifndef NDEBUG
C->dump();
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2517,6 +2517,9 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
if (const auto *Equiv = dyn_cast<DSOLocalEquivalent>(CV))
return getObjFileLowering().lowerDSOLocalEquivalent(Equiv, TM);

if (const NoCFIValue *NC = dyn_cast<NoCFIValue>(CV))
return MCSymbolRefExpr::create(getSymbol(NC->getGlobalValue()), Ctx);

const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV);
if (!CE) {
llvm_unreachable("Unknown constant value to lower!");
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,9 @@ SDValue SelectionDAGBuilder::getValueImpl(const Value *V) {
if (const auto *Equiv = dyn_cast<DSOLocalEquivalent>(C))
return getValue(Equiv->getGlobalValue());

if (const auto *NC = dyn_cast<NoCFIValue>(C))
return getValue(NC->getGlobalValue());

VectorType *VecTy = cast<VectorType>(V->getType());

// Now that we know the number and type of the elements, get that number of
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,12 @@ static void WriteConstantInternal(raw_ostream &Out, const Constant *CV,
return;
}

if (const auto *NC = dyn_cast<NoCFIValue>(CV)) {
Out << "no_cfi ";
WriteAsOperandInternal(Out, NC->getGlobalValue(), WriterCtx);
return;
}

if (const ConstantArray *CA = dyn_cast<ConstantArray>(CV)) {
Type *ETy = CA->getType()->getElementType();
Out << '[';
Expand Down
44 changes: 44 additions & 0 deletions llvm/lib/IR/Constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,9 @@ void llvm::deleteConstant(Constant *C) {
case Constant::DSOLocalEquivalentVal:
delete static_cast<DSOLocalEquivalent *>(C);
break;
case Constant::NoCFIValueVal:
delete static_cast<NoCFIValue *>(C);
break;
case Constant::UndefValueVal:
delete static_cast<UndefValue *>(C);
break;
Expand Down Expand Up @@ -1963,6 +1966,47 @@ Value *DSOLocalEquivalent::handleOperandChangeImpl(Value *From, Value *To) {
return nullptr;
}

NoCFIValue *NoCFIValue::get(GlobalValue *GV) {
NoCFIValue *&NC = GV->getContext().pImpl->NoCFIValues[GV];
if (!NC)
NC = new NoCFIValue(GV);

assert(NC->getGlobalValue() == GV &&
"NoCFIValue does not match the expected global value");
return NC;
}

NoCFIValue::NoCFIValue(GlobalValue *GV)
: Constant(GV->getType(), Value::NoCFIValueVal, &Op<0>(), 1) {
setOperand(0, GV);
}

/// Remove the constant from the constant table.
void NoCFIValue::destroyConstantImpl() {
const GlobalValue *GV = getGlobalValue();
GV->getContext().pImpl->NoCFIValues.erase(GV);
}

Value *NoCFIValue::handleOperandChangeImpl(Value *From, Value *To) {
assert(From == getGlobalValue() && "Changing value does not match operand.");

GlobalValue *GV = dyn_cast<GlobalValue>(To->stripPointerCasts());
assert(GV && "Can only replace the operands with a global value");

NoCFIValue *&NewNC = getContext().pImpl->NoCFIValues[GV];
if (NewNC)
return llvm::ConstantExpr::getBitCast(NewNC, getType());

getContext().pImpl->NoCFIValues.erase(getGlobalValue());
NewNC = this;
setOperand(0, GV);

if (GV->getType() != getType())
mutateType(GV->getType());

return nullptr;
}

//---- ConstantExpr::get() implementations.
//

Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/IR/LLVMContextImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1425,6 +1425,8 @@ class LLVMContextImpl {

DenseMap<const GlobalValue *, DSOLocalEquivalent *> DSOLocalEquivalents;

DenseMap<const GlobalValue *, NoCFIValue *> NoCFIValues;

ConstantUniqueMap<ConstantExpr> ExprConstants;

ConstantUniqueMap<InlineAsm> InlineAsms;
Expand Down
5 changes: 3 additions & 2 deletions llvm/lib/Transforms/IPO/LowerTypeTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1774,8 +1774,9 @@ void LowerTypeTestsModule::replaceCfiUses(Function *Old, Value *New,
bool IsJumpTableCanonical) {
SmallSetVector<Constant *, 4> Constants;
for (Use &U : llvm::make_early_inc_range(Old->uses())) {
// Skip block addresses
if (isa<BlockAddress>(U.getUser()))
// Skip block addresses and no_cfi values, which refer to the function
// body instead of the jump table.
if (isa<BlockAddress, NoCFIValue>(U.getUser()))
continue;

// Skip direct calls to externally defined or non-dso_local functions
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Transforms/Utils/ValueMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,12 @@ Value *Mapper::mapValue(const Value *V) {
DSOLocalEquivalent::get(Func), NewTy);
}

if (const auto *NC = dyn_cast<NoCFIValue>(C)) {
auto *Val = mapValue(NC->getGlobalValue());
GlobalValue *GV = cast<GlobalValue>(Val);
return getVM()[NC] = NoCFIValue::get(GV);
}

auto mapValueOrNull = [this](Value *V) {
auto Mapped = mapValue(V);
assert((Mapped || (Flags & RF_NullMapMissingGlobalValues)) &&
Expand Down
43 changes: 43 additions & 0 deletions llvm/test/Bitcode/nocfivalue.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
; RUN: llvm-as < %s | llvm-dis | FileCheck %s
; RUN: verify-uselistorder %s

; CHECK: @a = global [4 x void ()*] [void ()* no_cfi @f1, void ()* @f1, void ()* @f2, void ()* no_cfi @f2]
@a = global [4 x void ()*] [void ()* no_cfi @f1, void ()* @f1, void ()* @f2, void ()* no_cfi @f2]
; CHECK: @b = constant void ()* no_cfi @f3
@b = constant void ()* no_cfi @f3
; CHECK: @c = constant void ()* @f3
@c = constant void ()* @f3

; CHECK: declare void @f1()
declare void @f1()

; CHECK: declare void @f2()
declare void @f2()

; CHECK: define void @f3()
define void @f3() {
; CHECK: call void no_cfi @f4()
call void no_cfi @f4()
; CHECK: call void @f4()
call void @f4()
; CHECK: call void no_cfi @f5()
call void no_cfi @f5()
; CHECK: call void @f5()
call void @f5()
ret void
}

; CHECK: declare void @f4()
declare void @f4()

; CHECK: declare void @f5()
declare void @f5()

define void @g() {
%n = alloca void ()*, align 8
; CHECK: store void ()* no_cfi @f5, void ()** %n, align 8
store void ()* no_cfi @f5, void ()** %n, align 8
%1 = load void ()*, void ()** %n
call void %1()
ret void
}
37 changes: 37 additions & 0 deletions llvm/test/CodeGen/X86/nocfivalue.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
; RUN: opt -S -lowertypetests %s | llc -asm-verbose=false | FileCheck %s

target datalayout = "e-p:64:64"
target triple = "x86_64-unknown-linux-gnu"

; CHECK: a:
; CHECK-NEXT: .quad f1
; CHECK-NEXT: .quad .L.cfi.jumptable
; CHECK-NEXT: .quad .L.cfi.jumptable
; CHECK-NEXT: .quad f2
; CHECK-NEXT: .quad f3
; CHECK-NEXT: .quad f3.cfi
@a = global [6 x void ()*] [void ()* no_cfi @f1, void ()* @f1, void ()* @f2, void ()* no_cfi @f2, void ()* @f3, void ()* no_cfi @f3]

declare !type !0 void @f1()

define internal void @f2() !type !0 {
ret void
}

define void @f3() #0 !type !0 {
ret void
}

declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone

define i1 @foo(i8* %p) {
%x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1")
ret i1 %x
}

!llvm.module.flags = !{!1}

attributes #0 = { "cfi-canonical-jump-table" }

!0 = !{i32 0, !"typeid1"}
!1 = !{i32 4, !"CFI Canonical Jump Tables", i32 0}
36 changes: 36 additions & 0 deletions llvm/test/Transforms/LowerTypeTests/nocfivalue.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
; RUN: opt -S -lowertypetests < %s | FileCheck %s

target datalayout = "e-p:64:64"
target triple = "x86_64-unknown-linux-gnu"

; CHECK: @a = global [6 x void ()*] [void ()* no_cfi @f1, void ()* @.cfi.jumptable, void ()* bitcast ([8 x i8]* getelementptr inbounds ([3 x [8 x i8]], [3 x [8 x i8]]* bitcast (void ()* @.cfi.jumptable to [3 x [8 x i8]]*), i64 0, i64 1) to void ()*), void ()* no_cfi @f2, void ()* @f3, void ()* no_cfi @f3.cfi]
@a = global [6 x void ()*] [void ()* no_cfi @f1, void ()* @f1, void ()* @f2, void ()* no_cfi @f2, void ()* @f3, void ()* no_cfi @f3]

; CHECK: define void @f1()
define void @f1() !type !0 {
ret void
}

; CHECK: define internal void @f2()
define internal void @f2() !type !0 {
ret void
}

; CHECK: define hidden void @f3.cfi()
define void @f3() #0 !type !0 {
ret void
}

declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone

define i1 @foo(i8* %p) {
%x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1")
ret i1 %x
}

!llvm.module.flags = !{!1}

attributes #0 = { "cfi-canonical-jump-table" }

!0 = !{i32 0, !"typeid1"}
!1 = !{i32 4, !"CFI Canonical Jump Tables", i32 0}
2 changes: 1 addition & 1 deletion llvm/utils/emacs/llvm-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
;; Runtime preemption specifiers
"dso_preemptable" "dso_local" "dso_local_equivalent"

"gc" "atomic" "volatile" "personality" "prologue" "section") 'symbols) . font-lock-keyword-face)
"gc" "atomic" "no_cfi" "volatile" "personality" "prologue" "section") 'symbols) . font-lock-keyword-face)
;; Arithmetic and Logical Operators
`(,(regexp-opt '("add" "sub" "mul" "sdiv" "udiv" "urem" "srem" "and" "or" "xor"
"setne" "seteq" "setlt" "setgt" "setle" "setge") 'symbols) . font-lock-keyword-face)
Expand Down
1 change: 1 addition & 0 deletions llvm/utils/vim/syntax/llvm.vim
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ syn keyword llvmKeyword
\ nocallback
\ nocapture
\ nocf_check
\ no_cfi
\ noduplicate
\ nofree
\ noimplicitfloat
Expand Down