Commit bba77687 by Karl Schimpf

Fix bitcode parser to check type signatures of functions.

Before, type signatures of functions were only checked when called. This CL fixes this by checking all function signatures. BUG=None R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1579203002 .
parent 56958cb3
......@@ -880,6 +880,8 @@ void Converter::installGlobalDeclarations(Module *Mod) {
StrBuf << "\n Use flag -allow-externally-defined-symbols to override";
report_fatal_error(StrBuf.str());
}
if (!IceFunc->validateTypeSignature(Ctx))
report_fatal_error(IceFunc->getTypeSignatureError(Ctx));
GlobalDeclarationMap[&Func] = IceFunc;
}
// Install global variable declarations.
......
......@@ -60,6 +60,56 @@ void dumpCallingConv(Ice::Ostream &, llvm::CallingConv::ID CallingConv) {
namespace Ice {
const Intrinsics::FullIntrinsicInfo *
FunctionDeclaration::getIntrinsicInfo(const GlobalContext *Ctx,
bool *IsIntrinsic) const {
*IsIntrinsic = false;
if (!hasName())
return nullptr;
bool BadIntrinsic;
const Intrinsics::FullIntrinsicInfo *Info =
Ctx->getIntrinsicsInfo().find(getName(), BadIntrinsic);
*IsIntrinsic = Info || BadIntrinsic;
return Info;
}
bool FunctionDeclaration::validateRegularTypeSignature() const {
for (SizeT i = 0; i < Signature.getNumArgs(); ++i) {
if (!isCallParameterType(Signature.getArgType(i)))
return false;
}
return isCallReturnType(Signature.getReturnType());
}
bool FunctionDeclaration::validateIntrinsicTypeSignature(
const Intrinsics::FullIntrinsicInfo *Info) const {
if (Signature.getNumArgs() != Info->getNumArgs())
return false;
for (SizeT i = 0; i < Signature.getNumArgs(); ++i) {
if (Signature.getArgType(i) != Info->getArgType(i))
return false;
}
return Signature.getReturnType() == Info->getReturnType();
}
IceString FunctionDeclaration::getTypeSignatureError(const GlobalContext *Ctx) {
std::string Buffer;
llvm::raw_string_ostream StrBuf(Buffer);
StrBuf << "Invalid";
bool IsIntrinsic;
const Intrinsics::FullIntrinsicInfo *Info =
getIntrinsicInfo(Ctx, &IsIntrinsic);
if (IsIntrinsic && Info == nullptr) {
StrBuf << " intrinsic name: " << getName();
return StrBuf.str();
}
StrBuf << " type signature for";
if (IsIntrinsic)
StrBuf << " intrinsic";
StrBuf << " " << getName() << ": " << getSignature();
return StrBuf.str();
}
void FunctionDeclaration::dumpType(Ostream &Stream) const {
if (!Ice::BuildDefs::dump())
return;
......
......@@ -21,6 +21,7 @@
#include "IceDefs.h"
#include "IceGlobalContext.h"
#include "IceIntrinsics.h"
#include "IceTypes.h"
#ifdef __clang__
......@@ -156,6 +157,32 @@ public:
return verifyLinkageDefault(Ctx);
}
/// Validates that the type signature of the function is correct. Returns true
/// if valid.
bool validateTypeSignature(const GlobalContext *Ctx) const {
bool IsIntrinsic;
if (const Intrinsics::FullIntrinsicInfo *Info =
getIntrinsicInfo(Ctx, &IsIntrinsic))
return validateIntrinsicTypeSignature(Info);
return !IsIntrinsic && validateRegularTypeSignature();
}
/// Generates an error message describing why validateTypeSignature returns
/// false.
IceString getTypeSignatureError(const GlobalContext *Ctx);
/// Returns corresponding PNaCl intrisic information.
const Intrinsics::FullIntrinsicInfo *
getIntrinsicInfo(const GlobalContext *Ctx) const {
bool BadIntrinsic;
return getIntrinsicInfo(Ctx, &BadIntrinsic);
}
/// Same as above, except IsIntrinsic is true if the function is intrinsic
/// (even if not a PNaCl intrinsic).
const Intrinsics::FullIntrinsicInfo *
getIntrinsicInfo(const GlobalContext *Ctx, bool *IsIntrinsic) const;
private:
const Ice::FuncSigType Signature;
llvm::CallingConv::ID CallingConv;
......@@ -173,12 +200,15 @@ private:
}
bool isIntrinsicName(const GlobalContext *Ctx) const {
if (!hasName())
return false;
bool BadIntrinsic;
return Ctx->getIntrinsicsInfo().find(getName(), BadIntrinsic) &&
!BadIntrinsic;
bool IsIntrinsic;
getIntrinsicInfo(Ctx, &IsIntrinsic);
return IsIntrinsic;
}
bool validateRegularTypeSignature() const;
bool validateIntrinsicTypeSignature(
const Intrinsics::FullIntrinsicInfo *Info) const;
};
/// Models a global variable declaration, and its initializers.
......
......@@ -338,6 +338,8 @@ public:
installFunctionNames();
}
void verifyFunctionTypeSignatures();
void createValueIDs() {
assert(VariableDeclarations);
ValueIDConstants.reserve(VariableDeclarations->size() +
......@@ -637,6 +639,14 @@ Ice::Type TopLevelParser::convertToIceTypeError(Type *LLVMTy) {
return Ice::IceType_void;
}
void TopLevelParser::verifyFunctionTypeSignatures() {
const Ice::GlobalContext *Ctx = getTranslator().getContext();
for (Ice::FunctionDeclaration *FuncDecl : FunctionDeclarations) {
if (!FuncDecl->validateTypeSignature(Ctx))
Error(FuncDecl->getTypeSignatureError(Ctx));
}
}
// Base class for parsing blocks within the bitcode file. Note: Because this is
// the base class of block parsers, we generate error messages if ParseBlock or
// ParseRecord is not overridden in derived classes.
......@@ -2648,16 +2658,7 @@ void FunctionParser::ProcessRecord() {
}
// Check if this direct call is to an Intrinsic (starts with "llvm.")
bool BadIntrinsic;
IntrinsicInfo = getTranslator().getContext()->getIntrinsicsInfo().find(
Fcn->getName(), BadIntrinsic);
if (BadIntrinsic) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Invalid PNaCl intrinsic call to " << Fcn->getName();
Error(StrBuf.str());
IntrinsicInfo = nullptr;
}
IntrinsicInfo = Fcn->getIntrinsicInfo(getTranslator().getContext());
if (IntrinsicInfo && IntrinsicInfo->getNumArgs() != NumParams) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
......@@ -2712,16 +2713,14 @@ void FunctionParser::ProcessRecord() {
if (Signature)
verifyCallArgTypeMatches(Fcn, Index, OpType,
Signature->getArgType(Index));
if (IntrinsicInfo) {
verifyCallArgTypeMatches(Fcn, Index, OpType,
IntrinsicInfo->getArgType(Index));
} else if (!isCallParameterType(OpType)) {
else if (!isCallParameterType(OpType)) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Argument " << *Op << " of " << printName(Fcn)
<< " has invalid type: " << Op->getType();
Error(StrBuf.str());
appendErrorInstruction(ReturnType);
return;
}
}
......@@ -3007,6 +3006,7 @@ private:
if (!GlobalDeclarationNamesAndInitializersInstalled) {
Context->installGlobalNames();
Context->createValueIDs();
Context->verifyFunctionTypeSignatures();
std::unique_ptr<Ice::VariableDeclarationList> Globals =
Context->getGlobalVariables();
if (Globals)
......
......@@ -4,9 +4,10 @@
; RUN: %p2i --filetype=obj --disassemble -i %s --args -O2 | FileCheck %s
; RUN: %p2i --filetype=obj --disassemble -i %s --args -Om1 | FileCheck %s
define internal i32 @Sdiv_const8_b(i8 %a) {
define internal i32 @Sdiv_const8_b(i32 %a32) {
; CHECK-LABEL: Sdiv_const8_b
entry:
%a = trunc i32 %a32 to i8
%div = sdiv i8 %a, 12
; CHECK: mov {{.*}},0xc
; CHECK-NOT: idiv 0xc
......@@ -14,9 +15,10 @@ entry:
ret i32 %div_ext
}
define internal i32 @Sdiv_const16_b(i16 %a) {
define internal i32 @Sdiv_const16_b(i32 %a32) {
; CHECK-LABEL: Sdiv_const16_b
entry:
%a = trunc i32 %a32 to i16
%div = sdiv i16 %a, 1234
; CHECK: mov {{.*}},0x4d2
; CHECK-NOT: idiv 0x4d2
......
......@@ -40,8 +40,9 @@ entry:
; CHECK-LABEL: no_rmw_add_i32_var
; CHECK: add e{{ax|bx|cx|dx|bp|di|si}},DWORD PTR [e{{ax|bx|cx|dx|bp|di|si}}]
define internal void @rmw_add_i16_var(i32 %addr_arg, i16 %var) {
define internal void @rmw_add_i16_var(i32 %addr_arg, i32 %var32) {
entry:
%var = trunc i32 %var32 to i16
%addr = inttoptr i32 %addr_arg to i16*
%val = load i16, i16* %addr, align 1
%rmw = add i16 %val, %var
......@@ -64,8 +65,9 @@ entry:
; CHECK-LABEL: rmw_add_i16_imm
; CHECK: add WORD PTR [e{{ax|bx|cx|dx|bp|di|si}}],0x13
define internal void @rmw_add_i8_var(i32 %addr_arg, i8 %var) {
define internal void @rmw_add_i8_var(i32 %addr_arg, i32 %var32) {
entry:
%var = trunc i32 %var32 to i8
%addr = inttoptr i32 %addr_arg to i8*
%val = load i8, i8* %addr, align 1
%rmw = add i8 %val, %var
......
65535,8,2;
1,1;
65535,17,2;
1,5;
2;
7,32;
7,8;
21,0,0,1;
21,0,0;
65534;
8,3,0,1,0;
8,4,0,0,0;
65535,19,2;
5,0;
65534;
65535,14,2;
1,1,84,101,115,116;
1,0,102;
65534;
65535,12,2;
1,1;
65535,11,2;
1,1;
4,2;
1,2;
4,2;
65534;
2,1,1,0;
34,0,5,2;
10;
65534;
65534;
......@@ -8,7 +8,7 @@
; RUN: -allow-externally-defined-symbols 2>&1 \
; RUN: | FileCheck %s
; CHECK: Argument 1 of llvm.nacl.setjmp expects i32. Found: double
; CHECK: Invalid type signature for intrinsic llvm.nacl.setjmp: i32 (double)
; RUN: pnacl-bcfuzz -bitcode-as-text \
; RUN: %p/Inputs/bad-intrinsic-arg.tbc -output - \
......
; Test that even if a call parameter matches its declaration, it must still
; be a legal call parameter type (unless declaration is intrinsic).
; Test that a function parameter must be a legal parameter type (unless
; declaration is intrinsic).
; REQUIRES: no_minimal_build
......@@ -7,10 +7,10 @@
; RUN: -allow-externally-defined-symbols | FileCheck %s
declare void @f(i8);
; CHECK: Invalid type signature for f: void (i8)
define void @Test() {
entry:
call void @f(i8 1)
; CHECK: Argument 1 of f has invalid type: i8
ret void
}
; Show that we check parameter types of a function call against paramter types
; of called function.
; REQUIRES: no_minimal_build
; RUN: not %pnacl_sz -bitcode-as-text %p/Inputs/call-fcn-bad-param-type.tbc \
; RUN: -bitcode-format=pnacl -notranslate -build-on-read \
; RUN: -allow-externally-defined-symbols 2>&1 \
; RUN: | FileCheck %s
; RUN: pnacl-bcfuzz -bitcode-as-text -output - \
; RUN: %p/Inputs/call-fcn-bad-param-type.tbc \
; RUN: | not pnacl-bcdis -no-records | FileCheck %s --check-prefix=DIS
; DIS: module { // BlockID = 8
; DIS-NEXT: version 1;
; DIS-NEXT: types { // BlockID = 17
; DIS-NEXT: count 5;
; DIS-NEXT: @t0 = void;
; DIS-NEXT: @t1 = i32;
; DIS-NEXT: @t2 = i8;
; DIS-NEXT: @t3 = void (i32);
; DIS-NEXT: @t4 = void ();
; DIS-NEXT: }
; DIS-NEXT: declare external void @f0(i32);
; DIS-NEXT: define external void @f1();
; DIS-NEXT: globals { // BlockID = 19
; DIS-NEXT: count 0;
; DIS-NEXT: }
; DIS-NEXT: valuesymtab { // BlockID = 14
; DIS-NEXT: @f1 : "Test";
; DIS-NEXT: @f0 : "f";
; DIS-NEXT: }
; DIS-NEXT: function void @f1() { // BlockID = 12
; DIS-NEXT: blocks 1;
; DIS-NEXT: constants { // BlockID = 11
; DIS-NEXT: i32:
; DIS-NEXT: %c0 = i32 1;
; DIS-NEXT: i8:
; DIS-NEXT: %c1 = i8 1;
; DIS-NEXT: }
; DIS-NEXT: %b0:
; DIS-NEXT: %v0 = add i8 %c1, %c1;
; DIS-NEXT: call void @f0(i8 %c1);
; DIS-NEXT: Error({{.*}}): Parameter 1 mismatch: i8 and i32
; CHECK: Argument 1 of f expects i32. Found: i8
; DIS-NEXT: ret void;
; DIS-NEXT: }
; DIS-NEXT: }
......@@ -6,12 +6,15 @@
; RUN: %p2i --expect-fail -i %s --insts --args \
; RUN: -allow-externally-defined-symbols | FileCheck %s
declare i1 @f();
declare i32 @f();
define void @Test() {
declare i64 @g();
define void @Test(i32 %ifcn) {
entry:
%v = call i1 @f()
; CHECK: Return type of f is invalid: i1
%fcn = inttoptr i32 %ifcn to i1()*
%v = call i1 %fcn()
; CHECK: Return type of function is invalid: i1
ret void
}
; Test that even if a call parameter matches its declaration, it must still
; be a legal call parameter type (unless declaration is intrinsic).
; REQUIRES: no_minimal_build
; RUN: %p2i --expect-fail -i %s --insts --args \
; RUN: -allow-externally-defined-symbols | FileCheck %s
declare void @f(i8);
; CHECK: Invalid type signature for f: void (i8)
define void @Test() {
entry:
call void @f(i8 1)
ret void
}
; Tests that we don't get fooled by a fake NaCl intrinsic.
; TODO(kschimpf) Find way to run this through p2i. Note: Can't do this
; currently because run-pnacl-sz.py raises exception on error,
; and output is lost.
; RUN: %if --need=allow_dump --command llvm-as < %s \
; RUN: | %if --need=allow_dump --command pnacl-freeze \
; RUN -allow-local-symbol-tables \
; RUN: | %if --need=allow_dump --command not %pnacl_sz -notranslate \
; RUN: -verbose=inst -build-on-read \
; RUN: -allow-pnacl-reader-error-recovery \
; RUN: -allow-local-symbol-tables \
; RUN: -filetype=obj -o /dev/null \
; RUN: | %if --need=allow_dump --command FileCheck %s
; REQUIRES: allow_dump
; RUN: %if --need=no_dump --command llvm-as < %s \
; RUN: | %if --need=no_dump --command pnacl-freeze \
; RUN -allow-local-symbol-tables \
; RUN: | %if --need=no_dump --command not %pnacl_sz -notranslate \
; RUN: -verbose=inst -build-on-read \
; RUN: -allow-pnacl-reader-error-recovery \
; RUN: -allow-local-symbol-tables \
; RUN: -filetype=obj -o /dev/null \
; RUN: | %if --need=no_dump --command FileCheck %s --check-prefix=MIN
; RUN: %p2i --expect-fail -i %s --insts --args \
; RUN: -verbose=inst -allow-externally-defined-symbols \
; RUN: | FileCheck %s
declare i32 @llvm.fake.i32(i32)
define i32 @testFake(i32 %v) {
%r = call i32 @llvm.fake.i32(i32 %v)
ret i32 %r
}
; CHECK: Error({{.*}}): Invalid PNaCl intrinsic call to llvm.fake.i32
; MIN: Error({{.*}}): Invalid function record: <34 0 3 1>
; CHECK: Error({{.*}}): Invalid intrinsic name: llvm.fake.i32
......@@ -4,7 +4,7 @@
; RUN: %p2i -i %s --args -notranslate -timing | \
; RUN: FileCheck --check-prefix=NOIR %s
define internal i1 @IcmpI1(i32 %p1, i32 %p2) {
define internal void @IcmpI1(i32 %p1, i32 %p2) {
entry:
%a1 = trunc i32 %p1 to i1
%a2 = trunc i32 %p2 to i1
......@@ -18,10 +18,10 @@ entry:
%vsge = icmp sge i1 %a1, %a2
%vslt = icmp slt i1 %a1, %a2
%vsle = icmp sle i1 %a1, %a2
ret i1 %veq
ret void
}
; CHECK: define internal i1 @IcmpI1(i32 %p1, i32 %p2) {
; CHECK: define internal void @IcmpI1(i32 %p1, i32 %p2) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a1 = trunc i32 %p1 to i1
; CHECK-NEXT: %a2 = trunc i32 %p2 to i1
......@@ -35,10 +35,10 @@ entry:
; CHECK-NEXT: %vsge = icmp sge i1 %a1, %a2
; CHECK-NEXT: %vslt = icmp slt i1 %a1, %a2
; CHECK-NEXT: %vsle = icmp sle i1 %a1, %a2
; CHECK-NEXT: ret i1 %veq
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define internal i1 @IcmpI8(i32 %p1, i32 %p2) {
define internal void @IcmpI8(i32 %p1, i32 %p2) {
entry:
%a1 = trunc i32 %p1 to i8
%a2 = trunc i32 %p2 to i8
......@@ -52,10 +52,10 @@ entry:
%vsge = icmp sge i8 %a1, %a2
%vslt = icmp slt i8 %a1, %a2
%vsle = icmp sle i8 %a1, %a2
ret i1 %veq
ret void
}
; CHECK-NEXT: define internal i1 @IcmpI8(i32 %p1, i32 %p2) {
; CHECK-NEXT: define internal void @IcmpI8(i32 %p1, i32 %p2) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a1 = trunc i32 %p1 to i8
; CHECK-NEXT: %a2 = trunc i32 %p2 to i8
......@@ -69,10 +69,10 @@ entry:
; CHECK-NEXT: %vsge = icmp sge i8 %a1, %a2
; CHECK-NEXT: %vslt = icmp slt i8 %a1, %a2
; CHECK-NEXT: %vsle = icmp sle i8 %a1, %a2
; CHECK-NEXT: ret i1 %veq
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define internal i1 @IcmpI16(i32 %p1, i32 %p2) {
define internal void @IcmpI16(i32 %p1, i32 %p2) {
entry:
%a1 = trunc i32 %p1 to i16
%a2 = trunc i32 %p2 to i16
......@@ -86,10 +86,10 @@ entry:
%vsge = icmp sge i16 %a1, %a2
%vslt = icmp slt i16 %a1, %a2
%vsle = icmp sle i16 %a1, %a2
ret i1 %veq
ret void
}
; CHECK-NEXT: define internal i1 @IcmpI16(i32 %p1, i32 %p2) {
; CHECK-NEXT: define internal void @IcmpI16(i32 %p1, i32 %p2) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a1 = trunc i32 %p1 to i16
; CHECK-NEXT: %a2 = trunc i32 %p2 to i16
......@@ -103,10 +103,10 @@ entry:
; CHECK-NEXT: %vsge = icmp sge i16 %a1, %a2
; CHECK-NEXT: %vslt = icmp slt i16 %a1, %a2
; CHECK-NEXT: %vsle = icmp sle i16 %a1, %a2
; CHECK-NEXT: ret i1 %veq
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define internal i1 @IcmpI32(i32 %a1, i32 %a2) {
define internal void @IcmpI32(i32 %a1, i32 %a2) {
entry:
%veq = icmp eq i32 %a1, %a2
%vne = icmp ne i32 %a1, %a2
......@@ -118,10 +118,10 @@ entry:
%vsge = icmp sge i32 %a1, %a2
%vslt = icmp slt i32 %a1, %a2
%vsle = icmp sle i32 %a1, %a2
ret i1 %veq
ret void
}
; CHECK-NEXT: define internal i1 @IcmpI32(i32 %a1, i32 %a2) {
; CHECK-NEXT: define internal void @IcmpI32(i32 %a1, i32 %a2) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %veq = icmp eq i32 %a1, %a2
; CHECK-NEXT: %vne = icmp ne i32 %a1, %a2
......@@ -133,10 +133,10 @@ entry:
; CHECK-NEXT: %vsge = icmp sge i32 %a1, %a2
; CHECK-NEXT: %vslt = icmp slt i32 %a1, %a2
; CHECK-NEXT: %vsle = icmp sle i32 %a1, %a2
; CHECK-NEXT: ret i1 %veq
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define internal i1 @IcmpI64(i64 %a1, i64 %a2) {
define internal void @IcmpI64(i64 %a1, i64 %a2) {
entry:
%veq = icmp eq i64 %a1, %a2
%vne = icmp ne i64 %a1, %a2
......@@ -148,10 +148,10 @@ entry:
%vsge = icmp sge i64 %a1, %a2
%vslt = icmp slt i64 %a1, %a2
%vsle = icmp sle i64 %a1, %a2
ret i1 %veq
ret void
}
; CHECK-NEXT: define internal i1 @IcmpI64(i64 %a1, i64 %a2) {
; CHECK-NEXT: define internal void @IcmpI64(i64 %a1, i64 %a2) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %veq = icmp eq i64 %a1, %a2
; CHECK-NEXT: %vne = icmp ne i64 %a1, %a2
......@@ -163,7 +163,7 @@ entry:
; CHECK-NEXT: %vsge = icmp sge i64 %a1, %a2
; CHECK-NEXT: %vslt = icmp slt i64 %a1, %a2
; CHECK-NEXT: %vsle = icmp sle i64 %a1, %a2
; CHECK-NEXT: ret i1 %veq
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define internal <4 x i1> @IcmpV4xI1(<4 x i1> %a1, <4 x i1> %a2) {
......@@ -346,7 +346,7 @@ entry:
; CHECK-NEXT: ret <4 x i1> %veq
; CHECK-NEXT: }
define internal i1 @FcmpFloat(float %a1, float %a2) {
define internal void @FcmpFloat(float %a1, float %a2) {
entry:
%vfalse = fcmp false float %a1, %a2
%voeq = fcmp oeq float %a1, %a2
......@@ -364,10 +364,10 @@ entry:
%vune = fcmp une float %a1, %a2
%vuno = fcmp uno float %a1, %a2
%vtrue = fcmp true float %a1, %a2
ret i1 %voeq
ret void
}
; CHECK-NEXT: define internal i1 @FcmpFloat(float %a1, float %a2) {
; CHECK-NEXT: define internal void @FcmpFloat(float %a1, float %a2) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %vfalse = fcmp false float %a1, %a2
; CHECK-NEXT: %voeq = fcmp oeq float %a1, %a2
......@@ -385,10 +385,10 @@ entry:
; CHECK-NEXT: %vune = fcmp une float %a1, %a2
; CHECK-NEXT: %vuno = fcmp uno float %a1, %a2
; CHECK-NEXT: %vtrue = fcmp true float %a1, %a2
; CHECK-NEXT: ret i1 %voeq
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define internal i1 @FcmpDouble(double %a1, double %a2) {
define internal void @FcmpDouble(double %a1, double %a2) {
entry:
%vfalse = fcmp false double %a1, %a2
%voeq = fcmp oeq double %a1, %a2
......@@ -406,10 +406,10 @@ entry:
%vune = fcmp une double %a1, %a2
%vuno = fcmp uno double %a1, %a2
%vtrue = fcmp true double %a1, %a2
ret i1 %voeq
ret void
}
; CHECK-NEXT: define internal i1 @FcmpDouble(double %a1, double %a2) {
; CHECK-NEXT: define internal void @FcmpDouble(double %a1, double %a2) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %vfalse = fcmp false double %a1, %a2
; CHECK-NEXT: %voeq = fcmp oeq double %a1, %a2
......@@ -427,7 +427,7 @@ entry:
; CHECK-NEXT: %vune = fcmp une double %a1, %a2
; CHECK-NEXT: %vuno = fcmp uno double %a1, %a2
; CHECK-NEXT: %vtrue = fcmp true double %a1, %a2
; CHECK-NEXT: ret i1 %voeq
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define internal <4 x i1> @FcmpV4xFloat(<4 x float> %a1, <4 x float> %a2) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment