Skip to content

Commit cc3e556

Browse files
MrSidimsmshelego
authored andcommitted
[Backport to 14] Introduce CodeSectionINTEL storage class (KhronosGroup#2728)
This storage class is used for function pointers. It's added as based on cl_intel_function_pointers specification, it is not guaranteed that sizeof(void(*)(void) == sizeof(void *) - to allow consumers use this fact, we cannot say that function pointer belongs to the same storage class as data pointers. It wasn't added during initial implementation, now it's time to fill this gap. As it would be a breaking change its generation is added only under -spirv-emit-function-ptr-addr-space option. Also SPIR-V consumer may pass this option during reverse translation to get new address space even in a case, when OpConstantFunctionPointerINTEL doesn't reside in CodeSectionINTEL storage class. Expected behavior: No option is passed to the forward translation stage and function pointers are in addrspace(9): no CodeSectionINTEL storage class in SPIR-V The option is passed to the forward translation stage and function pointers are in addrepace(9): CodeSectionINTEL storage class is generated No option is passed to the reverse translation stage: function pointers are in private address space The option is passed to the reverse translation stage: function pointers are in addrspace(9) Spec: https://github.com/intel/llvm/blob/sycl/sycl/doc/design/spirv-extensions/SPV_INTEL_function_pointers.asciidoc The previous approach: KhronosGroup#1392
1 parent 0c70424 commit cc3e556

21 files changed

+1174
-12
lines changed

include/LLVMSPIRVOpts.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ class TranslatorOpts {
221221
PreserveOCLKernelArgTypeMetadataThroughString = Value;
222222
}
223223

224+
bool shouldEmitFunctionPtrAddrSpace() const noexcept {
225+
return EmitFunctionPtrAddrSpace;
226+
}
227+
228+
void setEmitFunctionPtrAddrSpace(bool Value) noexcept {
229+
EmitFunctionPtrAddrSpace = Value;
230+
}
231+
224232
private:
225233
// Common translation options
226234
VersionNumber MaxVersion = VersionNumber::MaximumVersion;
@@ -262,6 +270,10 @@ class TranslatorOpts {
262270
// kernel_arg_type_qual metadata through OpString
263271
bool PreserveOCLKernelArgTypeMetadataThroughString = false;
264272

273+
// Controls if CodeSectionINTEL can be emitted and consumed with a dedicated
274+
// address space
275+
bool EmitFunctionPtrAddrSpace = false;
276+
265277
bool PreserveAuxData = false;
266278
};
267279

lib/SPIRV/SPIRVInternal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ enum SPIRAddressSpace {
193193
SPIRAS_GlobalHost,
194194
SPIRAS_Input,
195195
SPIRAS_Output,
196+
SPIRAS_CodeSectionINTEL,
196197
SPIRAS_Count,
197198
};
198199

@@ -203,6 +204,8 @@ template <> inline void SPIRVMap<SPIRAddressSpace, std::string>::init() {
203204
add(SPIRAS_Local, "Local");
204205
add(SPIRAS_Generic, "Generic");
205206
add(SPIRAS_Input, "Input");
207+
add(SPIRAS_CodeSectionINTEL, "CodeSectionINTEL");
208+
206209
add(SPIRAS_GlobalDevice, "GlobalDevice");
207210
add(SPIRAS_GlobalHost, "GlobalHost");
208211
}
@@ -219,6 +222,7 @@ inline void SPIRVMap<SPIRAddressSpace, SPIRVStorageClassKind>::init() {
219222
add(SPIRAS_Input, StorageClassInput);
220223
add(SPIRAS_GlobalDevice, StorageClassDeviceOnlyINTEL);
221224
add(SPIRAS_GlobalHost, StorageClassHostOnlyINTEL);
225+
add(SPIRAS_CodeSectionINTEL, StorageClassCodeSectionINTEL);
222226
}
223227
typedef SPIRVMap<SPIRAddressSpace, SPIRVStorageClassKind> SPIRSPIRVAddrSpaceMap;
224228

lib/SPIRV/SPIRVReader.cpp

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,17 @@ Type *SPIRVToLLVM::transType(SPIRVType *T, bool IsClassMember) {
381381
}
382382
case internal::OpTypeTokenINTEL:
383383
return mapType(T, Type::getTokenTy(*Context));
384-
case OpTypePointer:
384+
case OpTypePointer: {
385+
unsigned AS = SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
386+
if (AS == SPIRAS_CodeSectionINTEL && !BM->shouldEmitFunctionPtrAddrSpace())
387+
AS = SPIRAS_Private;
388+
if (BM->shouldEmitFunctionPtrAddrSpace() &&
389+
T->getPointerElementType()->getOpCode() == OpTypeFunction)
390+
AS = SPIRAS_CodeSectionINTEL;
385391
return mapType(
386392
T, PointerType::get(
387-
transType(T->getPointerElementType(), IsClassMember),
388-
SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass())));
393+
transType(T->getPointerElementType(), IsClassMember), AS));
394+
}
389395
case OpTypeVector:
390396
return mapType(T,
391397
FixedVectorType::get(transType(T->getVectorComponentType()),
@@ -1463,10 +1469,20 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
14631469
case OpTypeVector:
14641470
return mapValue(BV, ConstantVector::get(CV));
14651471
case OpTypeMatrix:
1466-
case OpTypeArray:
1467-
return mapValue(
1468-
BV, ConstantArray::get(dyn_cast<ArrayType>(transType(BCC->getType())),
1469-
CV));
1472+
case OpTypeArray: {
1473+
auto *AT = cast<ArrayType>(transType(BCC->getType()));
1474+
for (size_t I = 0; I != AT->getNumElements(); ++I) {
1475+
auto *ElemTy = AT->getElementType();
1476+
if (auto *ElemPtrTy = dyn_cast<PointerType>(ElemTy)) {
1477+
assert(isa<PointerType>(CV[I]->getType()) &&
1478+
"Constant type doesn't match constexpr array element type");
1479+
if (ElemPtrTy->getAddressSpace() !=
1480+
cast<PointerType>(CV[I]->getType())->getAddressSpace())
1481+
CV[I] = ConstantExpr::getAddrSpaceCast(CV[I], AT->getElementType());
1482+
}
1483+
}
1484+
return mapValue(BV, ConstantArray::get(AT, CV));
1485+
}
14701486
case OpTypeStruct: {
14711487
auto BCCTy = dyn_cast<StructType>(transType(BCC->getType()));
14721488
auto Members = BCCTy->getNumElements();
@@ -1481,7 +1497,12 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
14811497
!BCCTy->getElementType(I)->isPointerTy())
14821498
continue;
14831499

1484-
CV[I] = ConstantExpr::getBitCast(CV[I], BCCTy->getElementType(I));
1500+
if (cast<PointerType>(CV[I]->getType())->getAddressSpace() !=
1501+
cast<PointerType>(BCCTy->getElementType(I))->getAddressSpace())
1502+
CV[I] =
1503+
ConstantExpr::getAddrSpaceCast(CV[I], BCCTy->getElementType(I));
1504+
else
1505+
CV[I] = ConstantExpr::getBitCast(CV[I], BCCTy->getElementType(I));
14851506
}
14861507
}
14871508

@@ -1519,7 +1540,10 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
15191540
static_cast<SPIRVConstantFunctionPointerINTEL *>(BV);
15201541
SPIRVFunction *F = BC->getFunction();
15211542
BV->setName(F->getName());
1522-
return mapValue(BV, transFunction(F));
1543+
const unsigned AS = BM->shouldEmitFunctionPtrAddrSpace()
1544+
? SPIRAS_CodeSectionINTEL
1545+
: SPIRAS_Private;
1546+
return mapValue(BV, transFunction(F, AS));
15231547
}
15241548

15251549
case OpUndef:
@@ -2994,7 +3018,7 @@ void SPIRVToLLVM::transFunctionAttrs(SPIRVFunction *BF, Function *F) {
29943018
});
29953019
}
29963020

2997-
Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF) {
3021+
Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF, unsigned AS) {
29983022
auto Loc = FuncMap.find(BF);
29993023
if (Loc != FuncMap.end())
30003024
return Loc->second;
@@ -3043,7 +3067,7 @@ Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF) {
30433067
}
30443068
Function *F = M->getFunction(FuncName);
30453069
if (!F)
3046-
F = Function::Create(FT, Linkage, FuncName, M);
3070+
F = Function::Create(FT, Linkage, AS, FuncName, M);
30473071
F = cast<Function>(mapValue(BF, F));
30483072
mapFunction(BF, F);
30493073

@@ -3440,6 +3464,17 @@ bool SPIRVToLLVM::translate() {
34403464
DbgTran->transDebugInst(EI);
34413465
}
34423466

3467+
for (auto *FP : BM->getFunctionPointers()) {
3468+
SPIRVConstantFunctionPointerINTEL *BC =
3469+
static_cast<SPIRVConstantFunctionPointerINTEL *>(FP);
3470+
SPIRVFunction *F = BC->getFunction();
3471+
FP->setName(F->getName());
3472+
const unsigned AS = BM->shouldEmitFunctionPtrAddrSpace()
3473+
? SPIRAS_CodeSectionINTEL
3474+
: SPIRAS_Private;
3475+
mapValue(FP, transFunction(F, AS));
3476+
}
3477+
34433478
for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) {
34443479
transFunction(BM->getFunction(I));
34453480
transUserSemantic(BM->getFunction(I));

lib/SPIRV/SPIRVReader.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#ifndef SPIRVREADER_H
4242
#define SPIRVREADER_H
4343

44+
#include "SPIRVInternal.h"
4445
#include "SPIRVModule.h"
4546

4647
#include "llvm/ADT/DenseMap.h"
@@ -95,7 +96,7 @@ class SPIRVToLLVM {
9596
void transAuxDataInst(SPIRVExtInst *BC);
9697
std::vector<Value *> transValue(const std::vector<SPIRVValue *> &,
9798
Function *F, BasicBlock *);
98-
Function *transFunction(SPIRVFunction *F);
99+
Function *transFunction(SPIRVFunction *F, unsigned AS = SPIRAS_Private);
99100
void transFunctionAttrs(SPIRVFunction *BF, Function *F);
100101
Value *transBlockInvoke(SPIRVValue *Invoke, BasicBlock *BB);
101102
Instruction *transWGSizeQueryBI(SPIRVInstruction *BI, BasicBlock *BB);

lib/SPIRV/SPIRVWriter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,11 @@ SPIRVType *LLVMToSPIRVBase::transPointerType(Type *ET, unsigned AddrSpc) {
509509
((AddrSpc == SPIRAS_GlobalDevice) || (AddrSpc == SPIRAS_GlobalHost))) {
510510
return transPointerType(ET, SPIRAS_Global);
511511
}
512+
// Lower function pointer address space to private if
513+
// spirv-emit-function-ptr-addr-space is not passed
514+
if (AddrSpc == SPIRAS_CodeSectionINTEL &&
515+
!BM->shouldEmitFunctionPtrAddrSpace())
516+
return transPointerType(ET, SPIRAS_Private);
512517
if (ST && !ST->isSized()) {
513518
Op OpCode;
514519
StringRef STName = ST->getName();

lib/SPIRV/libSPIRV/SPIRVInstruction.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ SPIRVInstruction *createInstFromSpecConstantOp(SPIRVSpecConstantOp *Inst) {
286286
auto OC = static_cast<Op>(Ops[0]);
287287
assert(isSpecConstantOpAllowedOp(OC) &&
288288
"Op code not allowed for OpSpecConstantOp");
289+
auto *Const = Inst->getOperand(1);
290+
// LLVM would eliminate a bitcast from a function pointer in a constexpr
291+
// context. Cut this short here to avoid necessity to align address spaces
292+
if (OC == OpBitcast && Const->getOpCode() == OpConstantFunctionPointerINTEL)
293+
return static_cast<SPIRVInstruction *>(Const);
289294
Ops.erase(Ops.begin(), Ops.begin() + 1);
290295
auto *BM = Inst->getModule();
291296
auto *RetInst = SPIRVInstTemplateBase::create(

lib/SPIRV/libSPIRV/SPIRVModule.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ class SPIRVModuleImpl : public SPIRVModule {
168168
SPIRVConstant *getLiteralAsConstant(unsigned Literal) override;
169169
unsigned getNumFunctions() const override { return FuncVec.size(); }
170170
unsigned getNumVariables() const override { return VariableVec.size(); }
171+
std::vector<SPIRVValue *> getFunctionPointers() const override {
172+
std::vector<SPIRVValue *> Res;
173+
for (auto *C : ConstVec)
174+
if (C->getOpCode() == OpConstantFunctionPointerINTEL)
175+
Res.emplace_back(C);
176+
return Res;
177+
}
171178
SourceLanguage getSourceLanguage(SPIRVWord *Ver = nullptr) const override {
172179
if (Ver)
173180
*Ver = SrcLangVer;

lib/SPIRV/libSPIRV/SPIRVModule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class SPIRVModule {
140140
virtual SPIRVMemoryModelKind getMemoryModel() const = 0;
141141
virtual unsigned getNumFunctions() const = 0;
142142
virtual unsigned getNumVariables() const = 0;
143+
virtual std::vector<SPIRVValue *> getFunctionPointers() const = 0;
143144
virtual SourceLanguage getSourceLanguage(SPIRVWord *) const = 0;
144145
virtual std::set<std::string> &getSourceExtension() = 0;
145146
virtual SPIRVValue *getValue(SPIRVId TheId) const = 0;
@@ -539,6 +540,10 @@ class SPIRVModule {
539540
.shouldPreserveOCLKernelArgTypeMetadataThroughString();
540541
}
541542

543+
bool shouldEmitFunctionPtrAddrSpace() const noexcept {
544+
return TranslationOpts.shouldEmitFunctionPtrAddrSpace();
545+
}
546+
542547
bool preserveAuxData() const noexcept {
543548
return TranslationOpts.preserveAuxData();
544549
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
; OpenCL C source:
2+
; char foo(char a) {
3+
; return a;
4+
; }
5+
; void bar() {
6+
; int (*fun_ptr)(int) = &foo;
7+
; }
8+
9+
; RUN: llvm-as %s -o %t.bc
10+
; RUN: llvm-spirv %t.bc -spirv-ext=+SPV_INTEL_function_pointers -o %t.spv
11+
; RUN: llvm-spirv %t.spv -to-text -o %t.spt
12+
; RUN: FileCheck < %t.spt %s --check-prefix=CHECK-SPIRV
13+
; RUN: llvm-spirv -r -spirv-emit-function-ptr-addr-space %t.spv -o %t.r.bc
14+
; RUN: llvm-dis %t.r.bc -o %t.r.ll
15+
; RUN: FileCheck < %t.r.ll %s --check-prefix=CHECK-LLVM
16+
17+
; CHECK-SPIRV: TypeFunction [[#FOO_TY:]] [[#]] [[#]]
18+
; CHECK-SPIRV: TypeFunction [[#DEST_TY:]] [[#]] [[#]]
19+
; CHECK-SPIRV: TypePointer [[#DEST_TY_PTR:]] [[#]] [[#DEST_TY]]
20+
; CHECK-SPIRV: TypePointer [[#FOO_TY_PTR:]] [[#]] [[#FOO_TY]]
21+
; CHECK-SPIRV: ConstantFunctionPointerINTEL [[#FOO_TY_PTR]] [[#FOO_PTR:]] [[#FOO:]]
22+
; CHECK-SPIRV: Function [[#]] [[#FOO]] [[#]] [[#FOO_TY]]
23+
24+
; CHECK-SPIRV: Bitcast [[#DEST_TY_PTR]] [[#]] [[#FOO_PTR]]
25+
26+
; CHECK-LLVM: bitcast i8 (i8) addrspace(9)* @foo to i32 (i32) addrspace(9)*
27+
28+
; ModuleID = './example.c'
29+
source_filename = "./example.c"
30+
target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
31+
target triple = "spir"
32+
33+
; Function Attrs: noinline nounwind optnone
34+
define dso_local spir_func signext i8 @foo(i8 signext %0) #0 {
35+
ret i8 %0
36+
}
37+
38+
; Function Attrs: noinline nounwind optnone
39+
define dso_local spir_func void @bar() #0 {
40+
%1 = alloca i32 (i32)*, align 4
41+
store i32 (i32)* bitcast (i8 (i8)* @foo to i32 (i32)*), i32 (i32)** %1, align 4
42+
ret void
43+
}
44+
45+
attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
46+
47+
!llvm.module.flags = !{!0}
48+
!llvm.ident = !{!1}
49+
50+
!0 = !{i32 1, !"wchar_size", i32 4}
51+
!1 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 0e1accd0f726eef2c47be9f37dd0a06cb50d207e)"}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
; RUN: llvm-as %s -o %t.bc
2+
; RUN: llvm-spirv %t.bc --spirv-ext=+SPV_INTEL_function_pointers -o %t.spv
3+
; RUN: llvm-spirv %t.spv -to-text -o - | FileCheck %s --check-prefix=CHECK-SPIRV
4+
; RUN: llvm-spirv -r -spirv-emit-function-ptr-addr-space %t.spv -o %t.r.bc
5+
; RUN: llvm-dis %t.r.bc -o %t.r.ll
6+
; RUN: FileCheck < %t.r.ll %s --check-prefix=CHECK-LLVM
7+
8+
; CHECK-SPIRV: Capability FunctionPointersINTEL
9+
; CHECK-SPIRV: Extension "SPV_INTEL_function_pointers"
10+
; CHECK-SPIRV: Name [[F1Name:[0-9]+]] "f1"
11+
; CHECK-SPIRV: Name [[F2Name:[0-9]+]] "f2"
12+
; CHECK-SPIRV: TypeInt [[Int32:[0-9]+]] 32
13+
; CHECK-SPIRV: TypeInt [[Int64:[0-9]+]] 64
14+
; CHECK-SPIRV-DAG: Constant [[Int32]] [[XArg:[0-9]+]] 32
15+
; CHECK-SPIRV-DAG: Constant [[Int32]] [[YArg:[0-9]+]] 2
16+
17+
; CHECK-SPIRV: ConstantFunctionPointerINTEL {{[0-9]+}} [[F1:[0-9]+]] [[F1Name]]
18+
; CHECK-SPIRV: ConstantFunctionPointerINTEL {{[0-9]+}} [[F2:[0-9]+]] [[F2Name]]
19+
; CHECK-SPIRV: ConstantComposite {{[0-9]+}} [[ConstComp:[0-9]+]] [[F1]] [[F2]]
20+
; CHECK-SPIRV: Variable {{[0-9]+}} [[Var:[0-9]+]] {{[0-9]+}} [[ConstComp]]
21+
22+
; CHECK-SPIRV: InBoundsPtrAccessChain {{[0-9]+}} [[GEP:[0-9]+]] [[Var]] {{[0-9]+}} {{[0-9]+}}
23+
; CHECK-SPIRV: Load {{[0-9]+}} [[FuncPtr:[0-9]+]] [[GEP]]
24+
; CHECK-SPIRV: FunctionPointerCallINTEL [[Int32]] {{[0-9]+}} [[FuncPtr]] [[XArg]] [[YArg]]
25+
26+
; CHECK-LLVM: @__const.main.funcs = internal constant [2 x i32 (i32, i32) addrspace(9)*] [i32 (i32, i32) addrspace(9)* @f1, i32 (i32, i32) addrspace(9)* @f2], align 16
27+
; CHECK-LLVM: %[[Idx:[a-z0-9]+]] = getelementptr inbounds [2 x i32 (i32, i32) addrspace(9)*], [2 x i32 (i32, i32) addrspace(9)*]* @__const.main.funcs, i64 0, i64 %{{[a-z0-9]+}}
28+
; CHECK-LLVM: %[[FuncPtr:[a-z0-9]+]] = load i32 (i32, i32) addrspace(9)*, i32 (i32, i32) addrspace(9)** %[[Idx]], align 8
29+
; CHECK-LLVM: %{{[a-z0-9]+}} = call spir_func addrspace(9) i32 %[[FuncPtr]](i32 32, i32 2)
30+
31+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
32+
target triple = "spir-unknown-unknown"
33+
34+
@__const.main.funcs = private unnamed_addr constant [2 x i32 (i32, i32)*] [i32 (i32, i32)* @f1, i32 (i32, i32)* @f2], align 16
35+
36+
; Function Attrs: norecurse nounwind readnone uwtable
37+
define dso_local i32 @f1(i32 %a, i32 %b) #0 {
38+
entry:
39+
%add = add nsw i32 %b, %a
40+
ret i32 %add
41+
}
42+
43+
; Function Attrs: norecurse nounwind readnone uwtable
44+
define dso_local i32 @f2(i32 %a, i32 %b) #0 {
45+
entry:
46+
%sub = sub nsw i32 %a, %b
47+
ret i32 %sub
48+
}
49+
50+
; Function Attrs: nounwind uwtable
51+
define dso_local i32 @main() local_unnamed_addr #1 {
52+
entry:
53+
%call = tail call i32 @rand() #3
54+
%rem = srem i32 %call, 2
55+
%idxprom = sext i32 %rem to i64
56+
%arrayidx = getelementptr inbounds [2 x i32 (i32, i32)*], [2 x i32 (i32, i32)*]* @__const.main.funcs, i64 0, i64 %idxprom
57+
%0 = load i32 (i32, i32)*, i32 (i32, i32)** %arrayidx, align 8
58+
%call1 = tail call i32 %0(i32 32, i32 2) #3
59+
ret i32 %call1
60+
}
61+
62+
; Function Attrs: nounwind
63+
declare dso_local i32 @rand() local_unnamed_addr #2
64+
65+
attributes #0 = { norecurse nounwind readnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
66+
attributes #1 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
67+
attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

0 commit comments

Comments
 (0)