Commit 928f1297 by Matt Wala

Add support for vector types.

- Add vector types to the type table. - Add support for parsing vector types in llvm2ice. - Legalize undef vector values to zero. Test that undef vector values are lowered correctly. BUG=none R=jvoung@chromium.org, stichnot@chromium.org Review URL: https://codereview.chromium.org/353553004
parent 8d7abae9
......@@ -165,6 +165,43 @@ private:
}
}
Ice::Type convertVectorType(const VectorType *VecTy) const {
unsigned NumElements = VecTy->getNumElements();
const Type *ElementType = VecTy->getElementType();
if (ElementType->isFloatTy()) {
if (NumElements == 4)
return Ice::IceType_v4f32;
} else if (ElementType->isIntegerTy()) {
switch (cast<IntegerType>(ElementType)->getBitWidth()) {
case 1:
if (NumElements == 4)
return Ice::IceType_v4i1;
if (NumElements == 8)
return Ice::IceType_v8i1;
if (NumElements == 16)
return Ice::IceType_v16i1;
break;
case 8:
if (NumElements == 16)
return Ice::IceType_v16i8;
break;
case 16:
if (NumElements == 8)
return Ice::IceType_v8i16;
break;
case 32:
if (NumElements == 4)
return Ice::IceType_v4i32;
break;
}
}
report_fatal_error(std::string("Unhandled vector type: ") +
LLVMObjectAsString(VecTy));
return Ice::IceType_void;
}
Ice::Type convertType(const Type *Ty) const {
switch (Ty->getTypeID()) {
case Type::VoidTyID:
......@@ -179,6 +216,8 @@ private:
return SubzeroPointerType;
case Type::FunctionTyID:
return SubzeroPointerType;
case Type::VectorTyID:
return convertVectorType(cast<VectorType>(Ty));
default:
report_fatal_error(std::string("Invalid PNaCl type: ") +
LLVMObjectAsString(Ty));
......
......@@ -232,6 +232,19 @@ Constant *GlobalContext::getConstantZero(Type Ty) {
return getConstantFloat(0);
case IceType_f64:
return getConstantDouble(0);
case IceType_v4i1:
case IceType_v8i1:
case IceType_v16i1:
case IceType_v16i8:
case IceType_v8i16:
case IceType_v4i32:
case IceType_v4f32: {
IceString Str;
llvm::raw_string_ostream BaseOS(Str);
Ostream OS(&BaseOS);
OS << "Unsupported constant type: " << Ty;
llvm_unreachable(BaseOS.str().c_str());
} break;
case IceType_void:
case IceType_NUM:
break;
......@@ -251,6 +264,19 @@ ConstantList GlobalContext::getConstantPool(Type Ty) const {
return ConstPool->Floats.getConstantPool();
case IceType_f64:
return ConstPool->Doubles.getConstantPool();
case IceType_v4i1:
case IceType_v8i1:
case IceType_v16i1:
case IceType_v16i8:
case IceType_v8i16:
case IceType_v4i32:
case IceType_v4f32: {
IceString Str;
llvm::raw_string_ostream BaseOS(Str);
Ostream OS(&BaseOS);
OS << "Unsupported constant type: " << Ty;
llvm_unreachable(BaseOS.str().c_str());
} break;
case IceType_void:
case IceType_NUM:
break;
......
......@@ -182,6 +182,11 @@ InstX8632Mov::InstX8632Mov(Cfg *Func, Variable *Dest, Operand *Source)
addSource(Source);
}
InstX8632Movp::InstX8632Movp(Cfg *Func, Variable *Dest, Operand *Source)
: InstX8632(Func, InstX8632::Movp, 1, Dest) {
addSource(Source);
}
InstX8632StoreQ::InstX8632StoreQ(Cfg *Func, Operand *Value, OperandX8632 *Mem)
: InstX8632(Func, InstX8632::StoreQ, 2, NULL) {
addSource(Value);
......@@ -222,6 +227,9 @@ InstX8632Push::InstX8632Push(Cfg *Func, Operand *Source,
}
bool InstX8632Mov::isRedundantAssign() const {
// TODO(stichnot): The isRedundantAssign() implementations for
// InstX8632Mov, InstX8632Movp, and InstX8632Movq are
// identical. Consolidate them.
Variable *Src = llvm::dyn_cast<Variable>(getSrc(0));
if (Src == NULL)
return false;
......@@ -237,6 +245,19 @@ bool InstX8632Mov::isRedundantAssign() const {
return false;
}
bool InstX8632Movp::isRedundantAssign() const {
Variable *Src = llvm::dyn_cast<Variable>(getSrc(0));
if (Src == NULL)
return false;
if (getDest()->hasReg() && getDest()->getRegNum() == Src->getRegNum()) {
return true;
}
if (!getDest()->hasReg() && !Src->hasReg() &&
Dest->getStackOffset() == Src->getStackOffset())
return true;
return false;
}
bool InstX8632Movq::isRedundantAssign() const {
Variable *Src = llvm::dyn_cast<Variable>(getSrc(0));
if (Src == NULL)
......@@ -381,6 +402,7 @@ template <> const char *InstX8632Sbb::Opcode = "sbb";
template <> const char *InstX8632And::Opcode = "and";
template <> const char *InstX8632Or::Opcode = "or";
template <> const char *InstX8632Xor::Opcode = "xor";
template <> const char *InstX8632Pxor::Opcode = "pxor";
template <> const char *InstX8632Imul::Opcode = "imul";
template <> const char *InstX8632Mulss::Opcode = "mulss";
template <> const char *InstX8632Div::Opcode = "div";
......@@ -693,6 +715,20 @@ void InstX8632Mov::dump(const Cfg *Func) const {
dumpSources(Func);
}
void InstX8632Movp::emit(const Cfg *Func) const {
// TODO(wala,stichnot): movups works with all vector operands, but
// there exist other instructions (movaps, movdqa, movdqu) that may
// perform better, depending on the data type and alignment of the
// operands.
Ostream &Str = Func->getContext()->getStrEmit();
assert(getSrcSize() == 1);
Str << "\tmovups\t";
getDest()->emit(Func);
Str << ", ";
getSrc(0)->emit(Func);
Str << "\n";
}
void InstX8632Movq::emit(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrEmit();
assert(getSrcSize() == 1);
......@@ -705,6 +741,14 @@ void InstX8632Movq::emit(const Cfg *Func) const {
Str << "\n";
}
void InstX8632Movp::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Str << "movups." << getDest()->getType() << " ";
dumpDest(Func);
Str << ", ";
dumpSources(Func);
}
void InstX8632Movq::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Str << "movq." << getDest()->getType() << " ";
......
......@@ -65,16 +65,23 @@
X(Br_p, "p", "jp") \
//#define X(tag, dump, emit)
#define ICETYPEX8632_TABLE \
/* tag, cvt, sdss, width */ \
X(IceType_void, "?", "" , "???") \
X(IceType_i1, "i", "" , "byte ptr") \
X(IceType_i8, "i", "" , "byte ptr") \
X(IceType_i16, "i", "" , "word ptr") \
X(IceType_i32, "i", "" , "dword ptr") \
X(IceType_i64, "i", "" , "qword ptr") \
X(IceType_f32, "s", "ss", "dword ptr") \
X(IceType_f64, "d", "sd", "qword ptr") \
#define ICETYPEX8632_TABLE \
/* tag, cvt, sdss, width */ \
X(IceType_void, "?", "" , "???") \
X(IceType_i1, "i", "" , "byte ptr") \
X(IceType_i8, "i", "" , "byte ptr") \
X(IceType_i16, "i", "" , "word ptr") \
X(IceType_i32, "i", "" , "dword ptr") \
X(IceType_i64, "i", "" , "qword ptr") \
X(IceType_f32, "s", "ss", "dword ptr") \
X(IceType_f64, "d", "sd", "qword ptr") \
X(IceType_v4i1, "?", "" , "xmmword ptr") \
X(IceType_v8i1, "?", "" , "xmmword ptr") \
X(IceType_v16i1, "?", "" , "xmmword ptr") \
X(IceType_v16i8, "?", "" , "xmmword ptr") \
X(IceType_v8i16, "?", "" , "xmmword ptr") \
X(IceType_v4i32, "?", "" , "xmmword ptr") \
X(IceType_v4f32, "?", "" , "xmmword ptr") \
//#define X(tag, cvt, sdss, width)
#endif // SUBZERO_SRC_ICEINSTX8632_DEF
......@@ -153,6 +153,7 @@ public:
Load,
Mfence,
Mov,
Movp,
Movq,
Movsx,
Movzx,
......@@ -161,6 +162,7 @@ public:
Or,
Pop,
Push,
Pxor,
Ret,
Sar,
Sbb,
......@@ -402,6 +404,7 @@ typedef InstX8632Binop<InstX8632::Sbb> InstX8632Sbb;
typedef InstX8632Binop<InstX8632::And> InstX8632And;
typedef InstX8632Binop<InstX8632::Or> InstX8632Or;
typedef InstX8632Binop<InstX8632::Xor> InstX8632Xor;
typedef InstX8632Binop<InstX8632::Pxor> InstX8632Pxor;
typedef InstX8632Binop<InstX8632::Imul> InstX8632Imul;
typedef InstX8632Binop<InstX8632::Mulss> InstX8632Mulss;
typedef InstX8632Binop<InstX8632::Divss> InstX8632Divss;
......@@ -638,6 +641,26 @@ private:
virtual ~InstX8632Mov() {}
};
// Move packed - copy 128 bit values between XMM registers or mem128 and
// XMM registers
class InstX8632Movp : public InstX8632 {
public:
static InstX8632Movp *create(Cfg *Func, Variable *Dest, Operand *Source) {
return new (Func->allocate<InstX8632Movp>())
InstX8632Movp(Func, Dest, Source);
}
virtual bool isRedundantAssign() const;
virtual void emit(const Cfg *Func) const;
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return isClassof(Inst, Movp); }
private:
InstX8632Movp(Cfg *Func, Variable *Dest, Operand *Source);
InstX8632Movp(const InstX8632Movp &) LLVM_DELETED_FUNCTION;
InstX8632Movp &operator=(const InstX8632Movp &) LLVM_DELETED_FUNCTION;
virtual ~InstX8632Movp() {}
};
// This is essentially a "movq" instruction with an OperandX8632Mem
// operand instead of Variable as the destination. It's important
// for liveness that there is no Dest operand.
......
......@@ -120,10 +120,9 @@ public:
}
T getValue() const { return Value; }
using Constant::emit;
virtual void emit(GlobalContext *Ctx) const {
Ostream &Str = Ctx->getStrEmit();
Str << getValue();
}
// The target needs to implement this for each ConstantPrimitive
// specialization.
virtual void emit(GlobalContext *Ctx) const;
using Constant::dump;
virtual void dump(GlobalContext *Ctx) const {
Ostream &Str = Ctx->getStrDump();
......
......@@ -160,7 +160,8 @@ void xMacroIntegrityCheck() {
_num
};
// Define a set of constants based on high-level table entries.
#define X(tag, size, align, str) static const int _table1_##tag = tag;
#define X(tag, size, align, elts, elty, str) \
static const int _table1_##tag = tag;
ICETYPE_TABLE;
#undef X
// Define a set of constants based on low-level table entries,
......@@ -172,7 +173,8 @@ void xMacroIntegrityCheck() {
#undef X
// Repeat the static asserts with respect to the high-level
// table entries in case the high-level table has extra entries.
#define X(tag, size, align, str) STATIC_ASSERT(_table1_##tag == _table2_##tag);
#define X(tag, size, align, elts, elty, str) \
STATIC_ASSERT(_table1_##tag == _table2_##tag);
ICETYPE_TABLE;
#undef X
}
......@@ -190,6 +192,7 @@ TargetX8632::TargetX8632(Cfg *Func)
llvm::SmallBitVector IntegerRegisters(Reg_NUM);
llvm::SmallBitVector IntegerRegistersI8(Reg_NUM);
llvm::SmallBitVector FloatRegisters(Reg_NUM);
llvm::SmallBitVector VectorRegisters(Reg_NUM);
llvm::SmallBitVector InvalidRegisters(Reg_NUM);
ScratchRegs.resize(Reg_NUM);
#define X(val, init, name, name16, name8, scratch, preserved, stackptr, \
......@@ -197,6 +200,7 @@ TargetX8632::TargetX8632(Cfg *Func)
IntegerRegisters[val] = isInt; \
IntegerRegistersI8[val] = isI8; \
FloatRegisters[val] = isFP; \
VectorRegisters[val] = isFP; \
ScratchRegs[val] = scratch;
REGX8632_TABLE;
#undef X
......@@ -208,6 +212,13 @@ TargetX8632::TargetX8632(Cfg *Func)
TypeToRegisterSet[IceType_i64] = IntegerRegisters;
TypeToRegisterSet[IceType_f32] = FloatRegisters;
TypeToRegisterSet[IceType_f64] = FloatRegisters;
TypeToRegisterSet[IceType_v4i1] = VectorRegisters;
TypeToRegisterSet[IceType_v8i1] = VectorRegisters;
TypeToRegisterSet[IceType_v16i1] = VectorRegisters;
TypeToRegisterSet[IceType_v16i8] = VectorRegisters;
TypeToRegisterSet[IceType_v8i16] = VectorRegisters;
TypeToRegisterSet[IceType_v4i32] = VectorRegisters;
TypeToRegisterSet[IceType_v4f32] = VectorRegisters;
}
void TargetX8632::translateO2() {
......@@ -1317,6 +1328,21 @@ void TargetX8632::lowerCall(const InstCall *Instr) {
// Leave eax==edx==NULL, and capture the result with the fstp
// instruction.
break;
case IceType_v4i1:
case IceType_v8i1:
case IceType_v16i1:
case IceType_v16i8:
case IceType_v8i16:
case IceType_v4i32:
case IceType_v4f32: {
// TODO(wala): Handle return values of vector type in the caller.
IceString Ty;
llvm::raw_string_ostream BaseOS(Ty);
Ostream OS(&BaseOS);
OS << Dest->getType();
Func->setError("Unhandled dest type: " + BaseOS.str());
return;
}
}
}
// TODO(stichnot): LEAHACK: remove Legal_All (and use default) once
......@@ -2273,6 +2299,8 @@ void TargetX8632::lowerRet(const InstRet *Inst) {
} else if (Src0->getType() == IceType_f32 ||
Src0->getType() == IceType_f64) {
_fld(Src0);
} else if (isVectorType(Src0->getType())) {
Reg = legalizeToVar(Src0, false, Reg_xmm0);
} else {
_mov(Reg, Src0, Reg_eax);
}
......@@ -2394,6 +2422,19 @@ void TargetX8632::lowerUnreachable(const InstUnreachable * /*Inst*/) {
lowerCall(Call);
}
// Helper for legalize() to emit the right code to lower an operand to a
// register of the appropriate type.
Variable *TargetX8632::copyToReg(Operand *Src, int32_t RegNum) {
Type Ty = Src->getType();
Variable *Reg = makeReg(Ty, RegNum);
if (isVectorType(Src->getType())) {
_movp(Reg, Src);
} else {
_mov(Reg, Src);
}
return Reg;
}
Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed,
bool AllowOverlap, int32_t RegNum) {
// Assert that a physical register is allowed. To date, all calls
......@@ -2426,9 +2467,7 @@ Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed,
}
if (!(Allowed & Legal_Mem)) {
Variable *Reg = makeReg(From->getType(), RegNum);
_mov(Reg, From, RegNum);
From = Reg;
From = copyToReg(From, RegNum);
}
return From;
}
......@@ -2445,7 +2484,19 @@ Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed,
// overestimated. If the constant being lowered is a 64 bit value,
// then the result should be split and the lo and hi components will
// need to go in uninitialized registers.
From = Ctx->getConstantZero(From->getType());
if (isVectorType(From->getType())) {
// There is no support for loading or emitting vector constants, so
// undef values are instead initialized in registers.
Variable *Reg = makeReg(From->getType(), RegNum);
// Insert a FakeDef, since otherwise the live range of Reg might
// be overestimated.
Context.insert(InstFakeDef::create(Func, Reg));
_pxor(Reg, Reg);
return Reg;
} else {
From = Ctx->getConstantZero(From->getType());
}
}
bool NeedsReg = false;
if (!(Allowed & Legal_Imm))
......@@ -2461,9 +2512,7 @@ Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed,
// On x86, FP constants are lowered to mem operands.
NeedsReg = true;
if (NeedsReg) {
Variable *Reg = makeReg(From->getType(), RegNum);
_mov(Reg, From);
From = Reg;
From = copyToReg(From, RegNum);
}
return From;
}
......@@ -2473,11 +2522,10 @@ Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed,
// RegNum is required and Var->getRegNum() doesn't match.
if ((!(Allowed & Legal_Mem) && !Var->hasReg()) ||
(RegNum != Variable::NoRegister && RegNum != Var->getRegNum())) {
Variable *Reg = makeReg(From->getType(), RegNum);
Variable *Reg = copyToReg(From, RegNum);
if (RegNum == Variable::NoRegister) {
Reg->setPreferredRegister(Var, AllowOverlap);
}
_mov(Reg, From);
From = Reg;
}
return From;
......@@ -2579,6 +2627,11 @@ void TargetX8632::postLower() {
}
}
template <> void ConstantInteger::emit(GlobalContext *Ctx) const {
Ostream &Str = Ctx->getStrEmit();
Str << getValue();
}
template <> void ConstantFloat::emit(GlobalContext *Ctx) const {
Ostream &Str = Ctx->getStrEmit();
// It would be better to prefix with ".L$" instead of "L$", but
......
......@@ -136,6 +136,8 @@ protected:
}
static Type stackSlotType();
Variable *copyToReg(Operand *Src, int32_t RegNum = Variable::NoRegister);
// The following are helpers that insert lowered x86 instructions
// with minimal syntactic overhead, so that the lowering code can
// look as close to assembly as practical.
......@@ -202,6 +204,9 @@ protected:
Context.insert(InstX8632Mov::create(Func, Dest, Src0));
}
}
void _movp(Variable *Dest, Operand *Src0) {
Context.insert(InstX8632Movp::create(Func, Dest, Src0));
}
void _movq(Variable *Dest, Operand *Src0) {
Context.insert(InstX8632Movq::create(Func, Dest, Src0));
}
......@@ -275,6 +280,9 @@ protected:
void _xor(Variable *Dest, Operand *Src0) {
Context.insert(InstX8632Xor::create(Func, Dest, Src0));
}
void _pxor(Variable *Dest, Operand *Src0) {
Context.insert(InstX8632Pxor::create(Func, Dest, Src0));
}
bool IsEbpBasedFrame;
size_t FrameSizeLocals;
......@@ -313,6 +321,7 @@ private:
virtual ~TargetGlobalInitX8632() {}
};
template <> void ConstantInteger::emit(GlobalContext *Ctx) const;
template <> void ConstantFloat::emit(GlobalContext *Ctx) const;
template <> void ConstantDouble::emit(GlobalContext *Ctx) const;
......
......@@ -21,10 +21,12 @@ namespace {
const struct {
size_t TypeWidthInBytes;
size_t TypeAlignInBytes;
size_t TypeNumElements;
Type TypeElementType;
const char *DisplayString;
} TypeAttributes[] = {
#define X(tag, size, align, str) \
{ size, align, str } \
#define X(tag, size, align, elts, elty, str) \
{ size, align, elts, elty, str } \
,
ICETYPE_TABLE
#undef X
......@@ -57,6 +59,28 @@ size_t typeAlignInBytes(Type Ty) {
return Align;
}
size_t typeNumElements(Type Ty) {
size_t NumElements = 0;
size_t Index = static_cast<size_t>(Ty);
if (Index < TypeAttributesSize) {
NumElements = TypeAttributes[Index].TypeNumElements;
} else {
llvm_unreachable("Invalid type for typeNumElements()");
}
return NumElements;
}
Type typeElementType(Type Ty) {
Type ElementType = IceType_void;
size_t Index = static_cast<size_t>(Ty);
if (Index < TypeAttributesSize) {
ElementType = TypeAttributes[Index].TypeElementType;
} else {
llvm_unreachable("Invalid type for typeElementType()");
}
return ElementType;
}
// ======================== Dump routines ======================== //
template <> Ostream &operator<<(Ostream &Str, const Type &Ty) {
......
......@@ -15,17 +15,24 @@
#ifndef SUBZERO_SRC_ICETYPES_DEF
#define SUBZERO_SRC_ICETYPES_DEF
#define ICETYPE_TABLE \
/* enum value, size, align, printable string */ \
/* (size and alignment in bytes) */ \
X(IceType_void, 0, 0, "void") \
X(IceType_i1, 1, 1, "i1") \
X(IceType_i8, 1, 1, "i8") \
X(IceType_i16, 2, 1, "i16") \
X(IceType_i32, 4, 1, "i32") \
X(IceType_i64, 8, 1, "i64") \
X(IceType_f32, 4, 4, "float") \
X(IceType_f64, 8, 8, "double") \
//#define X(tag, size, align, str)
#define ICETYPE_TABLE \
/* enum value, size, align, # elts, element type, printable string */ \
/* (size and alignment in bytes) */ \
X(IceType_void, 0, 0, 1, IceType_void, "void") \
X(IceType_i1, 1, 1, 1, IceType_i1, "i1") \
X(IceType_i8, 1, 1, 1, IceType_i8, "i8") \
X(IceType_i16, 2, 1, 1, IceType_i16, "i16") \
X(IceType_i32, 4, 1, 1, IceType_i32, "i32") \
X(IceType_i64, 8, 1, 1, IceType_i64, "i64") \
X(IceType_f32, 4, 4, 1, IceType_f32, "float") \
X(IceType_f64, 8, 8, 1, IceType_f64, "double") \
X(IceType_v4i1, 16, 1, 4, IceType_i1, "<4 x i1>") \
X(IceType_v8i1, 16, 1, 8, IceType_i1, "<8 x i1>") \
X(IceType_v16i1, 16, 1, 16, IceType_i1, "<16 x i1>") \
X(IceType_v16i8, 16, 1, 16, IceType_i8, "<16 x i8>") \
X(IceType_v8i16, 16, 2, 8, IceType_i16, "<8 x i16>") \
X(IceType_v4i32, 16, 4, 4, IceType_i32, "<4 x i32>") \
X(IceType_v4f32, 16, 4, 4, IceType_f32, "<4 x float>") \
//#define X(tag, size, align, elts, elty, str)
#endif // SUBZERO_SRC_ICETYPES_DEF
......@@ -21,7 +21,7 @@
namespace Ice {
enum Type {
#define X(tag, size, align, str) tag,
#define X(tag, size, align, elts, elty, str) tag,
ICETYPE_TABLE
#undef X
IceType_NUM
......@@ -43,6 +43,10 @@ enum OptLevel {
size_t typeWidthInBytes(Type Ty);
size_t typeAlignInBytes(Type Ty);
size_t typeNumElements(Type Ty);
Type typeElementType(Type Ty);
inline bool isVectorType(Type Ty) { return typeNumElements(Ty) > 1; }
template <> Ostream &operator<<(class Ostream &Str, const Type &Ty);
......
......@@ -7,31 +7,87 @@
; RUN: %llvm2iceinsts --pnacl %s | %szdiff %s \
; RUN: | FileCheck --check-prefix=DUMP %s
define i32 @undefi32() {
define i32 @undef_i32() {
entry:
; CHECK-LABEL: undefi32:
ret i32 undef
; CHECK-LABEL: undef_i32:
; CHECK: mov eax, 0
; CHECK: ret
}
define i64 @undefi64() {
define i64 @undef_i64() {
entry:
; CHECK-LABEL: undefi64:
ret i64 undef
; CHECK-LABEL: undef_i64:
; CHECK-DAG: mov eax, 0
; CHECK-DAG: mov edx, 0
; CHECK: ret
}
define float @undeffloat() {
define float @undef_float() {
entry:
; CHECK-LABEL: undeffloat:
ret float undef
; CHECK-LABEL: undef_float:
; CHECK-NOT: sub esp
; CHECK: fld
; CHECK: ret
}
define <4 x i1> @undef_v4i1() {
entry:
ret <4 x i1> undef
; CHECK-LABEL: undef_v4i1:
; CHECK: pxor
; CHECK: ret
}
define <8 x i1> @undef_v8i1() {
entry:
ret <8 x i1> undef
; CHECK-LABEL: undef_v8i1:
; CHECK: pxor
; CHECK: ret
}
define <16 x i1> @undef_v16i1() {
entry:
ret <16 x i1> undef
; CHECK-LABEL: undef_v16i1:
; CHECK: pxor
; CHECK: ret
}
define <16 x i8> @undef_v16i8() {
entry:
ret <16 x i8> undef
; CHECK-LABEL: undef_v16i8:
; CHECK: pxor
; CHECK: ret
}
define <8 x i16> @undef_v8i16() {
entry:
ret <8 x i16> undef
; CHECK-LABEL: undef_v8i16:
; CHECK: pxor
; CHECK: ret
}
define <4 x i32> @undef_v4i32() {
entry:
ret <4 x i32> undef
; CHECK-LABEL: undef_v4i32:
; CHECK: pxor
; CHECK: ret
}
define <4 x float> @undef_v4f32() {
entry:
ret <4 x float> undef
; CHECK-LABEL: undef_v4f32:
; CHECK: pxor
; CHECK: ret
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
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