Commit 8df26f3e by Karl Schimpf

Add call instructions to Subzero's bitcode reader.

parent bd385e46
...@@ -537,7 +537,8 @@ private: ...@@ -537,7 +537,8 @@ private:
// Not an intrinsic call. // Not an intrinsic call.
if (NewInst == NULL) { if (NewInst == NULL) {
NewInst = Ice::InstCall::create(Func, NumArgs, Dest, CallTarget); NewInst = Ice::InstCall::create(Func, NumArgs, Dest, CallTarget,
Inst->isTailCall());
} }
for (unsigned i = 0; i < NumArgs; ++i) { for (unsigned i = 0; i < NumArgs; ++i) {
NewInst->addArg(convertOperand(Inst, i)); NewInst->addArg(convertOperand(Inst, i));
...@@ -574,26 +575,38 @@ private: ...@@ -574,26 +575,38 @@ private:
void validateIntrinsicCall(const Ice::InstCall *Call, void validateIntrinsicCall(const Ice::InstCall *Call,
const Ice::Intrinsics::FullIntrinsicInfo *I) { const Ice::Intrinsics::FullIntrinsicInfo *I) {
assert(I->NumTypes >= 1); Ice::SizeT ArgIndex = 0;
if (I->Signature[0] == Ice::IceType_void) { switch (I->validateCall(Call, ArgIndex)) {
if (Call->getDest() != NULL) { default:
report_fatal_error( report_fatal_error("Unknown validation error for intrinsic call");
"Return value for intrinsic func w/ void return type."); break;
} case Ice::Intrinsics::IsValidCall:
} else { break;
if (I->Signature[0] != Call->getDest()->getType()) { case Ice::Intrinsics::BadReturnType: {
report_fatal_error("Mismatched return types."); std::string Buffer;
} raw_string_ostream StrBuf(Buffer);
StrBuf << "Intrinsic call expects return type " << I->getReturnType()
<< ". Found: " << Call->getReturnType();
report_fatal_error(StrBuf.str());
break;
} }
if (Call->getNumArgs() + 1 != I->NumTypes) { case Ice::Intrinsics::WrongNumOfArgs: {
std::cerr << "Call->getNumArgs() " << (int)Call->getNumArgs() std::string Buffer;
<< " I->NumTypes " << (int)I->NumTypes << "\n"; raw_string_ostream StrBuf(Buffer);
report_fatal_error("Mismatched # of args."); StrBuf << "Intrinsic call expects " << I->getNumArgs()
<< ". Found: " << Call->getNumArgs();
report_fatal_error(StrBuf.str());
break;
}
case Ice::Intrinsics::WrongCallArgType: {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Intrinsic call argument " << ArgIndex << " expects type "
<< I->getArgType(ArgIndex)
<< ". Found: " << Call->getArg(ArgIndex)->getType();
report_fatal_error(StrBuf.str());
break;
} }
for (size_t i = 1; i < I->NumTypes; ++i) {
if (Call->getArg(i - 1)->getType() != I->Signature[i]) {
report_fatal_error("Mismatched argument type.");
}
} }
} }
......
...@@ -566,6 +566,12 @@ void InstBr::dump(const Cfg *Func) const { ...@@ -566,6 +566,12 @@ void InstBr::dump(const Cfg *Func) const {
Str << "label %" << getTargetFalse()->getName(); Str << "label %" << getTargetFalse()->getName();
} }
Type InstCall::getReturnType() const {
if (Dest == NULL)
return IceType_void;
return Dest->getType();
}
void InstCall::dump(const Cfg *Func) const { void InstCall::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
if (getDest()) { if (getDest()) {
......
...@@ -296,32 +296,36 @@ private: ...@@ -296,32 +296,36 @@ private:
class InstCall : public Inst { class InstCall : public Inst {
public: public:
static InstCall *create(Cfg *Func, SizeT NumArgs, Variable *Dest, static InstCall *create(Cfg *Func, SizeT NumArgs, Variable *Dest,
Operand *CallTarget) { Operand *CallTarget, bool HasTailCall) {
// Set HasSideEffects to true so that the call instruction can't be // Set HasSideEffects to true so that the call instruction can't be
// dead-code eliminated. IntrinsicCalls can override this if the // dead-code eliminated. IntrinsicCalls can override this if the
// particular intrinsic is deletable and has no side-effects. // particular intrinsic is deletable and has no side-effects.
const bool HasSideEffects = true; const bool HasSideEffects = true;
const InstKind Kind = Inst::Call; const InstKind Kind = Inst::Call;
return new (Func->allocateInst<InstCall>()) return new (Func->allocateInst<InstCall>()) InstCall(
InstCall(Func, NumArgs, Dest, CallTarget, HasSideEffects, Kind); Func, NumArgs, Dest, CallTarget, HasTailCall, HasSideEffects, Kind);
} }
void addArg(Operand *Arg) { addSource(Arg); } void addArg(Operand *Arg) { addSource(Arg); }
Operand *getCallTarget() const { return getSrc(0); } Operand *getCallTarget() const { return getSrc(0); }
Operand *getArg(SizeT I) const { return getSrc(I + 1); } Operand *getArg(SizeT I) const { return getSrc(I + 1); }
SizeT getNumArgs() const { return getSrcSize() - 1; } SizeT getNumArgs() const { return getSrcSize() - 1; }
bool isTailcall() const { return HasTailCall; }
virtual void dump(const Cfg *Func) const; virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Call; } static bool classof(const Inst *Inst) { return Inst->getKind() == Call; }
Type getReturnType() const;
protected: protected:
InstCall(Cfg *Func, SizeT NumArgs, Variable *Dest, Operand *CallTarget, InstCall(Cfg *Func, SizeT NumArgs, Variable *Dest, Operand *CallTarget,
bool HasSideEff, InstKind Kind) bool HasTailCall, bool HasSideEff, InstKind Kind)
: Inst(Func, Kind, NumArgs + 1, Dest) { : Inst(Func, Kind, NumArgs + 1, Dest),
HasTailCall(HasTailCall) {
HasSideEffects = HasSideEff; HasSideEffects = HasSideEff;
addSource(CallTarget); addSource(CallTarget);
} }
virtual ~InstCall() {} virtual ~InstCall() {}
private: private:
bool HasTailCall;
InstCall(const InstCall &) LLVM_DELETED_FUNCTION; InstCall(const InstCall &) LLVM_DELETED_FUNCTION;
InstCall &operator=(const InstCall &) LLVM_DELETED_FUNCTION; InstCall &operator=(const InstCall &) LLVM_DELETED_FUNCTION;
}; };
...@@ -475,7 +479,7 @@ public: ...@@ -475,7 +479,7 @@ public:
private: private:
InstIntrinsicCall(Cfg *Func, SizeT NumArgs, Variable *Dest, InstIntrinsicCall(Cfg *Func, SizeT NumArgs, Variable *Dest,
Operand *CallTarget, const Intrinsics::IntrinsicInfo &Info) Operand *CallTarget, const Intrinsics::IntrinsicInfo &Info)
: InstCall(Func, NumArgs, Dest, CallTarget, Info.HasSideEffects, : InstCall(Func, NumArgs, Dest, CallTarget, false, Info.HasSideEffects,
Inst::IntrinsicCall), Inst::IntrinsicCall),
Info(Info) {} Info(Info) {}
InstIntrinsicCall(const InstIntrinsicCall &) LLVM_DELETED_FUNCTION; InstIntrinsicCall(const InstIntrinsicCall &) LLVM_DELETED_FUNCTION;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "IceCfg.h" #include "IceCfg.h"
#include "IceCfgNode.h" #include "IceCfgNode.h"
#include "IceInst.h"
#include "IceIntrinsics.h" #include "IceIntrinsics.h"
#include "IceLiveness.h" #include "IceLiveness.h"
#include "IceOperand.h" #include "IceOperand.h"
...@@ -226,4 +227,33 @@ bool Intrinsics::VerifyMemoryOrder(uint64_t Order) { ...@@ -226,4 +227,33 @@ bool Intrinsics::VerifyMemoryOrder(uint64_t Order) {
return Order == Intrinsics::MemoryOrderSequentiallyConsistent; return Order == Intrinsics::MemoryOrderSequentiallyConsistent;
} }
Intrinsics::ValidateCallValue
Intrinsics::FullIntrinsicInfo::validateCall(const Ice::InstCall *Call,
SizeT &ArgIndex) const {
assert(NumTypes >= 1);
Variable *Result = Call->getDest();
if (Result == NULL) {
if (Signature[0] != Ice::IceType_void)
return Intrinsics::BadReturnType;
} else if (Signature[0] != Result->getType()) {
return Intrinsics::BadReturnType;
}
if (Call->getNumArgs() + 1 != NumTypes) {
return Intrinsics::WrongNumOfArgs;
}
for (size_t i = 1; i < NumTypes; ++i) {
if (Call->getArg(i - 1)->getType() != Signature[i]) {
ArgIndex = i;
return Intrinsics::WrongCallArgType;
}
}
return Intrinsics::IsValidCall;
}
Type Intrinsics::FullIntrinsicInfo::getArgType(SizeT Index) const {
assert(NumTypes > 1);
assert(Index + 1 < NumTypes);
return Signature[Index + 1];
}
} // end of namespace Ice } // end of namespace Ice
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
namespace Ice { namespace Ice {
class InstCall;
static const size_t kMaxIntrinsicParameters = 6; static const size_t kMaxIntrinsicParameters = 6;
class Intrinsics { class Intrinsics {
...@@ -108,6 +110,14 @@ public: ...@@ -108,6 +110,14 @@ public:
enum ReturnsTwice ReturnsTwice : 1; enum ReturnsTwice ReturnsTwice : 1;
}; };
// The types of validation values for FullIntrinsicInfo.validateCall.
enum ValidateCallValue {
IsValidCall, // Valid use of instrinsic call.
BadReturnType, // Return type invalid for intrinsic.
WrongNumOfArgs, // Wrong number of arguments for intrinsic.
WrongCallArgType, // Argument of wrong type.
};
// The complete set of information about an intrinsic. // The complete set of information about an intrinsic.
struct FullIntrinsicInfo { struct FullIntrinsicInfo {
struct IntrinsicInfo Info; // Information that CodeGen would care about. struct IntrinsicInfo Info; // Information that CodeGen would care about.
...@@ -115,6 +125,27 @@ public: ...@@ -115,6 +125,27 @@ public:
// Sanity check during parsing. // Sanity check during parsing.
Type Signature[kMaxIntrinsicParameters]; Type Signature[kMaxIntrinsicParameters];
uint8_t NumTypes; uint8_t NumTypes;
// Validates that type signature of call matches intrinsic.
// If WrongArgumentType is returned, ArgIndex is set to corresponding
// argument index.
ValidateCallValue validateCall(const Ice::InstCall *Call,
SizeT &ArgIndex) const;
// Returns the return type of the intrinsic.
Type getReturnType() const {
assert(NumTypes > 1);
return Signature[0];
}
// Returns number of arguments expected.
SizeT getNumArgs() const {
assert(NumTypes > 1);
return NumTypes - 1;
}
// Returns type of Index-th argument.
Type getArgType(SizeT Index) const;
}; };
// Find the information about a given intrinsic, based on function name. // Find the information about a given intrinsic, based on function name.
......
...@@ -161,7 +161,7 @@ protected: ...@@ -161,7 +161,7 @@ protected:
const Type FunctionPointerType = IceType_i32; const Type FunctionPointerType = IceType_i32;
Constant *CallTarget = Constant *CallTarget =
Ctx->getConstantSym(FunctionPointerType, 0, Name, SuppressMangling); Ctx->getConstantSym(FunctionPointerType, 0, Name, SuppressMangling);
InstCall *Call = InstCall::create(Func, MaxSrcs, Dest, CallTarget); InstCall *Call = InstCall::create(Func, MaxSrcs, Dest, CallTarget, false);
return Call; return Call;
} }
static Type stackSlotType(); static Type stackSlotType();
......
...@@ -63,6 +63,7 @@ public: ...@@ -63,6 +63,7 @@ public:
NumFunctionBlocks(0), NumFunctionBlocks(0),
GlobalVarPlaceHolderType(convertToLLVMType(Ice::IceType_i8)) { GlobalVarPlaceHolderType(convertToLLVMType(Ice::IceType_i8)) {
Mod->setDataLayout(PNaClDataLayout); Mod->setDataLayout(PNaClDataLayout);
setErrStream(Translator.getContext()->getStrDump());
} }
virtual ~TopLevelParser() {} virtual ~TopLevelParser() {}
...@@ -146,6 +147,26 @@ public: ...@@ -146,6 +147,26 @@ public:
return ValueIDValues[ID]; return ValueIDValues[ID];
} }
/// Returns the corresponding constant associated with a global value
/// (i.e. relocatable).
Ice::Constant *getOrCreateGlobalConstantByID(unsigned ID) {
// TODO(kschimpf): Can this be built when creating global initializers?
if (ID >= ValueIDConstants.size()) {
if (ID >= ValueIDValues.size())
return NULL;
ValueIDConstants.resize(ValueIDValues.size());
}
Ice::Constant *C = ValueIDConstants[ID];
if (C != NULL)
return C;
Value *V = ValueIDValues[ID];
assert(isa<GlobalValue>(V));
C = getTranslator().getContext()->getConstantSym(getIcePointerType(), 0,
V->getName());
ValueIDConstants[ID] = C;
return C;
}
/// Returns the number of function addresses (i.e. ID's) defined in /// Returns the number of function addresses (i.e. ID's) defined in
/// the bitcode file. /// the bitcode file.
unsigned getNumFunctionIDs() const { return NumFunctionIds; } unsigned getNumFunctionIDs() const { return NumFunctionIds; }
...@@ -247,6 +268,8 @@ private: ...@@ -247,6 +268,8 @@ private:
std::vector<Type *> TypeIDValues; std::vector<Type *> TypeIDValues;
// The (global) value IDs. // The (global) value IDs.
std::vector<WeakVH> ValueIDValues; std::vector<WeakVH> ValueIDValues;
// Relocatable constants associated with ValueIDValues.
std::vector<Ice::Constant *> ValueIDConstants;
// The number of function IDs. // The number of function IDs.
unsigned NumFunctionIds; unsigned NumFunctionIds;
// The number of function blocks (processed so far). // The number of function blocks (processed so far).
...@@ -973,8 +996,7 @@ private: ...@@ -973,8 +996,7 @@ private:
// Returns the value referenced by the given value Index. // Returns the value referenced by the given value Index.
Ice::Operand *getOperand(uint32_t Index) { Ice::Operand *getOperand(uint32_t Index) {
if (Index < CachedNumGlobalValueIDs) { if (Index < CachedNumGlobalValueIDs) {
// TODO(kschimpf): Define implementation. return Context->getOrCreateGlobalConstantByID(Index);
report_fatal_error("getOperand of global addresses not implemented");
} }
uint32_t LocalIndex = Index - CachedNumGlobalValueIDs; uint32_t LocalIndex = Index - CachedNumGlobalValueIDs;
if (LocalIndex >= LocalOperands.size()) { if (LocalIndex >= LocalOperands.size()) {
...@@ -1123,6 +1145,18 @@ private: ...@@ -1123,6 +1145,18 @@ private:
// is not understood. // is not understood.
void ReportInvalidBinopOpcode(unsigned Opcode, Ice::Type Ty); void ReportInvalidBinopOpcode(unsigned Opcode, Ice::Type Ty);
// Returns true if the Str begins with Prefix.
bool isStringPrefix(Ice::IceString &Str, Ice::IceString &Prefix) {
const size_t PrefixSize = Prefix.size();
if (Str.size() < PrefixSize)
return false;
for (size_t i = 0; i < PrefixSize; ++i) {
if (Str[i] != Prefix[i])
return false;
}
return true;
}
// Takes the PNaCl bitcode binary operator Opcode, and the opcode // Takes the PNaCl bitcode binary operator Opcode, and the opcode
// type Ty, and sets Op to the corresponding ICE binary // type Ty, and sets Op to the corresponding ICE binary
// opcode. Returns true if able to convert, false otherwise. // opcode. Returns true if able to convert, false otherwise.
...@@ -1834,6 +1868,143 @@ void FunctionParser::ProcessRecord() { ...@@ -1834,6 +1868,143 @@ void FunctionParser::ProcessRecord() {
Ice::InstStore::create(Func, Value, Address, Alignment)); Ice::InstStore::create(Func, Value, Address, Alignment));
break; break;
} }
case naclbitc::FUNC_CODE_INST_CALL:
case naclbitc::FUNC_CODE_INST_CALL_INDIRECT: {
// CALL: [cc, fnid, arg0, arg1...]
// CALL_INDIRECT: [cc, fn, returnty, args...]
//
// Note: The difference between CALL and CALL_INDIRECT is that
// CALL has an explicit function address, while the CALL_INDIRECT
// is just an address. For CALL, we can infer the return type by
// looking up the type signature associated with the function
// address. For CALL_INDIRECT we can only infer the type signature
// via argument types, and the corresponding return type stored in
// CALL_INDIRECT record.
Ice::SizeT ParamsStartIndex = 2;
if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) {
if (!isValidRecordSizeAtLeast(2, "function block call"))
return;
} else {
if (!isValidRecordSizeAtLeast(3, "function block call indirect"))
return;
ParamsStartIndex = 3;
}
// Extract call information.
uint64_t CCInfo = Values[0];
CallingConv::ID CallingConv;
if (!naclbitc::DecodeCallingConv(CCInfo >> 1, CallingConv)) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Function call calling convention value " << (CCInfo >> 1)
<< " not understood.";
Error(StrBuf.str());
return;
}
bool IsTailCall = static_cast<bool>(CCInfo & 1);
// Extract out the called function and its return type.
uint32_t CalleeIndex = convertRelativeToAbsIndex(Values[1], BaseIndex);
Ice::Operand *Callee = getOperand(CalleeIndex);
Ice::Type ReturnType = Ice::IceType_void;
const Ice::Intrinsics::FullIntrinsicInfo *IntrinsicInfo = NULL;
if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) {
Function *Fcn =
dyn_cast<Function>(Context->getGlobalValueByID(CalleeIndex));
if (Fcn == NULL) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Function call to non-function: " << *Callee;
Error(StrBuf.str());
return;
}
FunctionType *FcnTy = Fcn->getFunctionType();
ReturnType = Context->convertToIceType(FcnTy->getReturnType());
// Check if this direct call is to an Intrinsic (starts with "llvm.")
static Ice::IceString LLVMPrefix("llvm.");
Ice::IceString Name = Fcn->getName();
if (isStringPrefix(Name, LLVMPrefix)) {
Ice::IceString Suffix = Name.substr(LLVMPrefix.size());
IntrinsicInfo =
getTranslator().getContext()->getIntrinsicsInfo().find(Suffix);
if (!IntrinsicInfo) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Invalid PNaCl intrinsic call to " << Name;
Error(StrBuf.str());
return;
}
}
} else {
ReturnType = Context->convertToIceType(Context->getTypeByID(Values[2]));
}
// Create the call instruction.
Ice::Variable *Dest =
(ReturnType == Ice::IceType_void) ? NULL : getNextInstVar(ReturnType);
Ice::SizeT NumParams = Values.size() - ParamsStartIndex;
Ice::InstCall *Inst = NULL;
if (IntrinsicInfo) {
Inst =
Ice::InstIntrinsicCall::create(Func, NumParams, Dest, Callee,
IntrinsicInfo->Info);
} else {
Inst = Ice::InstCall::create(Func, NumParams, Dest, Callee, IsTailCall);
}
// Add parameters.
for (Ice::SizeT ParamIndex = 0; ParamIndex < NumParams; ++ParamIndex) {
Inst->addArg(
getRelativeOperand(Values[ParamsStartIndex + ParamIndex], BaseIndex));
}
// If intrinsic call, validate call signature.
if (IntrinsicInfo) {
Ice::SizeT ArgIndex = 0;
switch (IntrinsicInfo->validateCall(Inst, ArgIndex)) {
default:
Error("Unknown validation error for intrinsic call");
// TODO(kschimpf) Remove error recovery once implementation complete.
break;
case Ice::Intrinsics::IsValidCall:
break;
case Ice::Intrinsics::BadReturnType: {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Intrinsic call expects return type "
<< IntrinsicInfo->getReturnType()
<< ". Found: " << Inst->getReturnType();
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
break;
}
case Ice::Intrinsics::WrongNumOfArgs: {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Intrinsic call expects " << IntrinsicInfo->getNumArgs()
<< ". Found: " << Inst->getNumArgs();
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
break;
}
case Ice::Intrinsics::WrongCallArgType: {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Intrinsic call argument " << ArgIndex << " expects type "
<< IntrinsicInfo->getArgType(ArgIndex)
<< ". Found: " << Inst->getArg(ArgIndex)->getType();
Error(StrBuf.str());
// TODO(kschimpf) Remove error recovery once implementation complete.
break;
}
}
}
CurrentNode->appendInst(Inst);
return;
}
case naclbitc::FUNC_CODE_INST_FORWARDTYPEREF: { case naclbitc::FUNC_CODE_INST_FORWARDTYPEREF: {
// FORWARDTYPEREF: [opval, ty] // FORWARDTYPEREF: [opval, ty]
if (!isValidRecordSize(2, "function block forward type ref")) if (!isValidRecordSize(2, "function block forward type ref"))
...@@ -1842,8 +2013,6 @@ void FunctionParser::ProcessRecord() { ...@@ -1842,8 +2013,6 @@ void FunctionParser::ProcessRecord() {
Context->getTypeByID(Values[1])))); Context->getTypeByID(Values[1]))));
break; break;
} }
case naclbitc::FUNC_CODE_INST_CALL:
case naclbitc::FUNC_CODE_INST_CALL_INDIRECT:
default: default:
// Generate error message! // Generate error message!
BlockParserBaseClass::ProcessRecord(); BlockParserBaseClass::ProcessRecord();
......
; Test parsing indirect calls in Subzero.
; RUN: llvm-as < %s | pnacl-freeze -allow-local-symbol-tables \
; RUN: | %llvm2ice -notranslate -verbose=inst -build-on-read \
; RUN: -allow-pnacl-reader-error-recovery \
; RUN: -allow-local-symbol-tables \
; RUN: | FileCheck %s
define internal void @CallIndirectVoid(i32 %f_addr) {
entry:
%f = inttoptr i32 %f_addr to void ()*
call void %f()
ret void
}
; CHECK: define internal void @CallIndirectVoid(i32 %f_addr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void %f_addr()
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define internal i32 @CallIndirectI32(i32 %f_addr) {
entry:
%f = inttoptr i32 %f_addr to i32(i8, i1)*
%r = call i32 %f(i8 1, i1 false)
ret i32 %r
}
; CHECK-NEXT: define internal i32 @CallIndirectI32(i32 %f_addr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i32 %f_addr(i8 1, i1 false)
; CHECK-NEXT: ret i32 %r
; CHECK-NEXT: }
; Test handling of call instructions.
; RUN: llvm-as < %s | pnacl-freeze -allow-local-symbol-tables \
; RUN: | %llvm2ice -notranslate -verbose=inst -build-on-read \
; RUN: -allow-pnacl-reader-error-recovery \
; RUN: -allow-local-symbol-tables \
; RUN: | FileCheck %s
define i32 @fib(i32 %n) {
entry:
%cmp = icmp slt i32 %n, 2
br i1 %cmp, label %return, label %if.end
if.end: ; preds = %entry
%sub = add i32 %n, -1
%call = tail call i32 @fib(i32 %sub)
%sub1 = add i32 %n, -2
%call2 = tail call i32 @fib(i32 %sub1)
%add = add i32 %call2, %call
ret i32 %add
return: ; preds = %entry
ret i32 %n
}
; CHECK: define i32 @fib(i32 %n) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %cmp = icmp slt i32 %n, 2
; CHECK-NEXT: br i1 %cmp, label %return, label %if.end
; CHECK-NEXT: if.end:
; CHECK-NEXT: %sub = add i32 %n, -1
; CHECK-NEXT: %call = call i32 @fib(i32 %sub)
; CHECK-NEXT: %sub1 = add i32 %n, -2
; CHECK-NEXT: %call2 = call i32 @fib(i32 %sub1)
; CHECK-NEXT: %add = add i32 %call2, %call
; CHECK-NEXT: ret i32 %add
; CHECK-NEXT: return:
; CHECK-NEXT: ret i32 %n
; CHECK-NEXT: }
define i32 @fact(i32 %n) {
entry:
%cmp = icmp slt i32 %n, 2
br i1 %cmp, label %return, label %if.end
if.end: ; preds = %entry
%sub = add i32 %n, -1
%call = tail call i32 @fact(i32 %sub)
%mul = mul i32 %call, %n
ret i32 %mul
return: ; preds = %entry
ret i32 %n
}
; CHECK-NEXT: define i32 @fact(i32 %n) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %cmp = icmp slt i32 %n, 2
; CHECK-NEXT: br i1 %cmp, label %return, label %if.end
; CHECK-NEXT: if.end:
; CHECK-NEXT: %sub = add i32 %n, -1
; CHECK-NEXT: %call = call i32 @fact(i32 %sub)
; CHECK-NEXT: %mul = mul i32 %call, %n
; CHECK-NEXT: ret i32 %mul
; CHECK-NEXT: return:
; CHECK-NEXT: ret i32 %n
; CHECK-NEXT: }
define i32 @redirect(i32 %n) {
entry:
%call = tail call i32 @redirect_target(i32 %n)
ret i32 %call
}
; CHECK-NEXT: define i32 @redirect(i32 %n) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %call = call i32 @redirect_target(i32 %n)
; CHECK-NEXT: ret i32 %call
; CHECK-NEXT: }
declare i32 @redirect_target(i32)
define void @call_void(i32 %n) {
entry:
%cmp2 = icmp sgt i32 %n, 0
br i1 %cmp2, label %if.then, label %if.end
if.then: ; preds = %entry, %if.then
%n.tr3 = phi i32 [ %call.i, %if.then ], [ %n, %entry ]
%sub = add i32 %n.tr3, -1
%call.i = tail call i32 @redirect_target(i32 %sub)
%cmp = icmp sgt i32 %call.i, 0
br i1 %cmp, label %if.then, label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
; CHECK-NEXT: define void @call_void(i32 %n) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %cmp2 = icmp sgt i32 %n, 0
; CHECK-NEXT: br i1 %cmp2, label %if.then, label %if.end
; CHECK-NEXT: if.then:
; CHECK-NEXT: %n.tr3 = phi i32 [ %call.i, %if.then ], [ %n, %entry ]
; CHECK-NEXT: %sub = add i32 %n.tr3, -1
; CHECK-NEXT: %call.i = call i32 @redirect_target(i32 %sub)
; CHECK-NEXT: %cmp = icmp sgt i32 %call.i, 0
; CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end
; CHECK-NEXT: if.end:
; CHECK-NEXT: ret void
; CHECK-NEXT: }
; Test parsing NaCl atomic instructions.
; RUN: llvm-as < %s | pnacl-freeze -allow-local-symbol-tables \
; RUN: | %llvm2ice -notranslate -verbose=inst -build-on-read \
; RUN: -allow-pnacl-reader-error-recovery \
; RUN: -allow-local-symbol-tables \
; RUN: | FileCheck %s
declare i8 @llvm.nacl.atomic.load.i8(i8*, i32)
declare i16 @llvm.nacl.atomic.load.i16(i16*, i32)
declare i32 @llvm.nacl.atomic.load.i32(i32*, i32)
declare i64 @llvm.nacl.atomic.load.i64(i64*, i32)
declare void @llvm.nacl.atomic.store.i8(i8, i8*, i32)
declare void @llvm.nacl.atomic.store.i16(i16, i16*, i32)
declare void @llvm.nacl.atomic.store.i32(i32, i32*, i32)
declare void @llvm.nacl.atomic.store.i64(i64, i64*, i32)
declare i8 @llvm.nacl.atomic.rmw.i8(i32, i8*, i8, i32)
declare i16 @llvm.nacl.atomic.rmw.i16(i32, i16*, i16, i32)
declare i32 @llvm.nacl.atomic.rmw.i32(i32, i32*, i32, i32)
declare i64 @llvm.nacl.atomic.rmw.i64(i32, i64*, i64, i32)
declare i8 @llvm.nacl.atomic.cmpxchg.i8(i8*, i8, i8, i32, i32)
declare i16 @llvm.nacl.atomic.cmpxchg.i16(i16*, i16, i16, i32, i32)
declare i32 @llvm.nacl.atomic.cmpxchg.i32(i32*, i32, i32, i32, i32)
declare i64 @llvm.nacl.atomic.cmpxchg.i64(i64*, i64, i64, i32, i32)
declare void @llvm.nacl.atomic.fence(i32)
declare void @llvm.nacl.atomic.fence.all()
declare i1 @llvm.nacl.atomic.is.lock.free(i32, i8*)
;;; Load
define i32 @test_atomic_load_8(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i8*
; parameter value "6" is for the sequential consistency memory order.
%i = call i8 @llvm.nacl.atomic.load.i8(i8* %ptr, i32 6)
%r = zext i8 %i to i32
ret i32 %r
}
; CHECK: define i32 @test_atomic_load_8(i32 %iptr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %i = call i8 @llvm.nacl.atomic.load.i8(i32 %iptr, i32 6)
; CHECK-NEXT: %r = zext i8 %i to i32
; CHECK-NEXT: ret i32 %r
; CHECK-NEXT: }
define i32 @test_atomic_load_16(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i16*
%i = call i16 @llvm.nacl.atomic.load.i16(i16* %ptr, i32 6)
%r = zext i16 %i to i32
ret i32 %r
}
; CHECK-NEXT: define i32 @test_atomic_load_16(i32 %iptr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %i = call i16 @llvm.nacl.atomic.load.i16(i32 %iptr, i32 6)
; CHECK-NEXT: %r = zext i16 %i to i32
; CHECK-NEXT: ret i32 %r
; CHECK-NEXT: }
define i32 @test_atomic_load_32(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%r = call i32 @llvm.nacl.atomic.load.i32(i32* %ptr, i32 6)
ret i32 %r
}
; CHECK-NEXT: define i32 @test_atomic_load_32(i32 %iptr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i32 @llvm.nacl.atomic.load.i32(i32 %iptr, i32 6)
; CHECK-NEXT: ret i32 %r
; CHECK-NEXT: }
define i64 @test_atomic_load_64(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%r = call i64 @llvm.nacl.atomic.load.i64(i64* %ptr, i32 6)
ret i64 %r
}
; CHECK-NEXT: define i64 @test_atomic_load_64(i32 %iptr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i64 @llvm.nacl.atomic.load.i64(i32 %iptr, i32 6)
; CHECK-NEXT: ret i64 %r
; CHECK-NEXT: }
;;; Store
define void @test_atomic_store_8(i32 %iptr, i32 %v) {
entry:
%truncv = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
call void @llvm.nacl.atomic.store.i8(i8 %truncv, i8* %ptr, i32 6)
ret void
}
; CHECK-NEXT: define void @test_atomic_store_8(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %truncv = trunc i32 %v to i8
; CHECK-NEXT: call void @llvm.nacl.atomic.store.i8(i8 %truncv, i32 %iptr, i32 6)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define void @test_atomic_store_16(i32 %iptr, i32 %v) {
entry:
%truncv = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
call void @llvm.nacl.atomic.store.i16(i16 %truncv, i16* %ptr, i32 6)
ret void
}
; CHECK-NEXT: define void @test_atomic_store_16(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %truncv = trunc i32 %v to i16
; CHECK-NEXT: call void @llvm.nacl.atomic.store.i16(i16 %truncv, i32 %iptr, i32 6)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define void @test_atomic_store_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
call void @llvm.nacl.atomic.store.i32(i32 %v, i32* %ptr, i32 6)
ret void
}
; CHECK-NEXT: define void @test_atomic_store_32(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @llvm.nacl.atomic.store.i32(i32 %v, i32 %iptr, i32 6)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define void @test_atomic_store_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
call void @llvm.nacl.atomic.store.i64(i64 %v, i64* %ptr, i32 6)
ret void
}
; CHECK-NEXT: define void @test_atomic_store_64(i32 %iptr, i64 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @llvm.nacl.atomic.store.i64(i64 %v, i32 %iptr, i32 6)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define void @test_atomic_store_64_const(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i64*
call void @llvm.nacl.atomic.store.i64(i64 12345678901234, i64* %ptr, i32 6)
ret void
}
; CHECK-NEXT: define void @test_atomic_store_64_const(i32 %iptr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @llvm.nacl.atomic.store.i64(i64 12345678901234, i32 %iptr, i32 6)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
;;; RMW
;; add
define i32 @test_atomic_rmw_add_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
; "1" is an atomic add, and "6" is sequential consistency.
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 1, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_add_8(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i8
; CHECK-NEXT: %a = call i8 @llvm.nacl.atomic.rmw.i8(i32 1, i32 %iptr, i8 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i8 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_add_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 1, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_add_16(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i16
; CHECK-NEXT: %a = call i16 @llvm.nacl.atomic.rmw.i16(i32 1, i32 %iptr, i16 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i16 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_add_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 1, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-NEXT: define i32 @test_atomic_rmw_add_32(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i32 @llvm.nacl.atomic.rmw.i32(i32 1, i32 %iptr, i32 %v, i32 6)
; CHECK-NEXT: ret i32 %a
; CHECK-NEXT: }
define i64 @test_atomic_rmw_add_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 1, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-NEXT: define i64 @test_atomic_rmw_add_64(i32 %iptr, i64 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i64 @llvm.nacl.atomic.rmw.i64(i32 1, i32 %iptr, i64 %v, i32 6)
; CHECK-NEXT: ret i64 %a
; CHECK-NEXT: }
;; sub
define i32 @test_atomic_rmw_sub_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 2, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_sub_8(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i8
; CHECK-NEXT: %a = call i8 @llvm.nacl.atomic.rmw.i8(i32 2, i32 %iptr, i8 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i8 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_sub_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 2, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_sub_16(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i16
; CHECK-NEXT: %a = call i16 @llvm.nacl.atomic.rmw.i16(i32 2, i32 %iptr, i16 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i16 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_sub_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 2, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-NEXT: define i32 @test_atomic_rmw_sub_32(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i32 @llvm.nacl.atomic.rmw.i32(i32 2, i32 %iptr, i32 %v, i32 6)
; CHECK-NEXT: ret i32 %a
; CHECK-NEXT: }
define i64 @test_atomic_rmw_sub_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 2, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-NEXT: define i64 @test_atomic_rmw_sub_64(i32 %iptr, i64 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i64 @llvm.nacl.atomic.rmw.i64(i32 2, i32 %iptr, i64 %v, i32 6)
; CHECK-NEXT: ret i64 %a
; CHECK-NEXT: }
;; or
define i32 @test_atomic_rmw_or_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 3, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_or_8(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i8
; CHECK-NEXT: %a = call i8 @llvm.nacl.atomic.rmw.i8(i32 3, i32 %iptr, i8 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i8 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_or_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 3, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_or_16(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i16
; CHECK-NEXT: %a = call i16 @llvm.nacl.atomic.rmw.i16(i32 3, i32 %iptr, i16 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i16 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_or_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 3, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-NEXT: define i32 @test_atomic_rmw_or_32(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i32 @llvm.nacl.atomic.rmw.i32(i32 3, i32 %iptr, i32 %v, i32 6)
; CHECK-NEXT: ret i32 %a
; CHECK-NEXT: }
define i64 @test_atomic_rmw_or_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 3, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-NEXT: define i64 @test_atomic_rmw_or_64(i32 %iptr, i64 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i64 @llvm.nacl.atomic.rmw.i64(i32 3, i32 %iptr, i64 %v, i32 6)
; CHECK-NEXT: ret i64 %a
; CHECK-NEXT: }
;; and
define i32 @test_atomic_rmw_and_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 4, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_and_8(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i8
; CHECK-NEXT: %a = call i8 @llvm.nacl.atomic.rmw.i8(i32 4, i32 %iptr, i8 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i8 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_and_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 4, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_and_16(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i16
; CHECK-NEXT: %a = call i16 @llvm.nacl.atomic.rmw.i16(i32 4, i32 %iptr, i16 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i16 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_and_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 4, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-NEXT: define i32 @test_atomic_rmw_and_32(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i32 @llvm.nacl.atomic.rmw.i32(i32 4, i32 %iptr, i32 %v, i32 6)
; CHECK-NEXT: ret i32 %a
; CHECK-NEXT: }
define i64 @test_atomic_rmw_and_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 4, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-NEXT: define i64 @test_atomic_rmw_and_64(i32 %iptr, i64 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i64 @llvm.nacl.atomic.rmw.i64(i32 4, i32 %iptr, i64 %v, i32 6)
; CHECK-NEXT: ret i64 %a
; CHECK-NEXT: }
;; xor
define i32 @test_atomic_rmw_xor_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 5, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_xor_8(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i8
; CHECK-NEXT: %a = call i8 @llvm.nacl.atomic.rmw.i8(i32 5, i32 %iptr, i8 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i8 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_xor_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 5, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_xor_16(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i16
; CHECK-NEXT: %a = call i16 @llvm.nacl.atomic.rmw.i16(i32 5, i32 %iptr, i16 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i16 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_xor_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 5, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-NEXT: define i32 @test_atomic_rmw_xor_32(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i32 @llvm.nacl.atomic.rmw.i32(i32 5, i32 %iptr, i32 %v, i32 6)
; CHECK-NEXT: ret i32 %a
; CHECK-NEXT: }
define i64 @test_atomic_rmw_xor_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 5, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-NEXT: define i64 @test_atomic_rmw_xor_64(i32 %iptr, i64 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i64 @llvm.nacl.atomic.rmw.i64(i32 5, i32 %iptr, i64 %v, i32 6)
; CHECK-NEXT: ret i64 %a
; CHECK-NEXT: }
;; exchange
define i32 @test_atomic_rmw_xchg_8(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i8
%ptr = inttoptr i32 %iptr to i8*
%a = call i8 @llvm.nacl.atomic.rmw.i8(i32 6, i8* %ptr, i8 %trunc, i32 6)
%a_ext = zext i8 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_xchg_8(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i8
; CHECK-NEXT: %a = call i8 @llvm.nacl.atomic.rmw.i8(i32 6, i32 %iptr, i8 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i8 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_xchg_16(i32 %iptr, i32 %v) {
entry:
%trunc = trunc i32 %v to i16
%ptr = inttoptr i32 %iptr to i16*
%a = call i16 @llvm.nacl.atomic.rmw.i16(i32 6, i16* %ptr, i16 %trunc, i32 6)
%a_ext = zext i16 %a to i32
ret i32 %a_ext
}
; CHECK-NEXT: define i32 @test_atomic_rmw_xchg_16(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc = trunc i32 %v to i16
; CHECK-NEXT: %a = call i16 @llvm.nacl.atomic.rmw.i16(i32 6, i32 %iptr, i16 %trunc, i32 6)
; CHECK-NEXT: %a_ext = zext i16 %a to i32
; CHECK-NEXT: ret i32 %a_ext
; CHECK-NEXT: }
define i32 @test_atomic_rmw_xchg_32(i32 %iptr, i32 %v) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%a = call i32 @llvm.nacl.atomic.rmw.i32(i32 6, i32* %ptr, i32 %v, i32 6)
ret i32 %a
}
; CHECK-NEXT: define i32 @test_atomic_rmw_xchg_32(i32 %iptr, i32 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i32 @llvm.nacl.atomic.rmw.i32(i32 6, i32 %iptr, i32 %v, i32 6)
; CHECK-NEXT: ret i32 %a
; CHECK-NEXT: }
define i64 @test_atomic_rmw_xchg_64(i32 %iptr, i64 %v) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%a = call i64 @llvm.nacl.atomic.rmw.i64(i32 6, i64* %ptr, i64 %v, i32 6)
ret i64 %a
}
; CHECK-NEXT: define i64 @test_atomic_rmw_xchg_64(i32 %iptr, i64 %v) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a = call i64 @llvm.nacl.atomic.rmw.i64(i32 6, i32 %iptr, i64 %v, i32 6)
; CHECK-NEXT: ret i64 %a
; CHECK-NEXT: }
;;;; Cmpxchg
define i32 @test_atomic_cmpxchg_8(i32 %iptr, i32 %expected, i32 %desired) {
entry:
%trunc_exp = trunc i32 %expected to i8
%trunc_des = trunc i32 %desired to i8
%ptr = inttoptr i32 %iptr to i8*
%old = call i8 @llvm.nacl.atomic.cmpxchg.i8(i8* %ptr, i8 %trunc_exp,
i8 %trunc_des, i32 6, i32 6)
%old_ext = zext i8 %old to i32
ret i32 %old_ext
}
; CHECK-NEXT: define i32 @test_atomic_cmpxchg_8(i32 %iptr, i32 %expected, i32 %desired) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc_exp = trunc i32 %expected to i8
; CHECK-NEXT: %trunc_des = trunc i32 %desired to i8
; CHECK-NEXT: %old = call i8 @llvm.nacl.atomic.cmpxchg.i8(i32 %iptr, i8 %trunc_exp, i8 %trunc_des, i32 6, i32 6)
; CHECK-NEXT: %old_ext = zext i8 %old to i32
; CHECK-NEXT: ret i32 %old_ext
; CHECK-NEXT: }
define i32 @test_atomic_cmpxchg_16(i32 %iptr, i32 %expected, i32 %desired) {
entry:
%trunc_exp = trunc i32 %expected to i16
%trunc_des = trunc i32 %desired to i16
%ptr = inttoptr i32 %iptr to i16*
%old = call i16 @llvm.nacl.atomic.cmpxchg.i16(i16* %ptr, i16 %trunc_exp,
i16 %trunc_des, i32 6, i32 6)
%old_ext = zext i16 %old to i32
ret i32 %old_ext
}
; CHECK-NEXT: define i32 @test_atomic_cmpxchg_16(i32 %iptr, i32 %expected, i32 %desired) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %trunc_exp = trunc i32 %expected to i16
; CHECK-NEXT: %trunc_des = trunc i32 %desired to i16
; CHECK-NEXT: %old = call i16 @llvm.nacl.atomic.cmpxchg.i16(i32 %iptr, i16 %trunc_exp, i16 %trunc_des, i32 6, i32 6)
; CHECK-NEXT: %old_ext = zext i16 %old to i32
; CHECK-NEXT: ret i32 %old_ext
; CHECK-NEXT: }
define i32 @test_atomic_cmpxchg_32(i32 %iptr, i32 %expected, i32 %desired) {
entry:
%ptr = inttoptr i32 %iptr to i32*
%old = call i32 @llvm.nacl.atomic.cmpxchg.i32(i32* %ptr, i32 %expected,
i32 %desired, i32 6, i32 6)
ret i32 %old
}
; CHECK-NEXT: define i32 @test_atomic_cmpxchg_32(i32 %iptr, i32 %expected, i32 %desired) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %old = call i32 @llvm.nacl.atomic.cmpxchg.i32(i32 %iptr, i32 %expected, i32 %desired, i32 6, i32 6)
; CHECK-NEXT: ret i32 %old
; CHECK-NEXT: }
define i64 @test_atomic_cmpxchg_64(i32 %iptr, i64 %expected, i64 %desired) {
entry:
%ptr = inttoptr i32 %iptr to i64*
%old = call i64 @llvm.nacl.atomic.cmpxchg.i64(i64* %ptr, i64 %expected,
i64 %desired, i32 6, i32 6)
ret i64 %old
}
; CHECK-NEXT: define i64 @test_atomic_cmpxchg_64(i32 %iptr, i64 %expected, i64 %desired) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %old = call i64 @llvm.nacl.atomic.cmpxchg.i64(i32 %iptr, i64 %expected, i64 %desired, i32 6, i32 6)
; CHECK-NEXT: ret i64 %old
; CHECK-NEXT: }
;;;; Fence and is-lock-free.
define void @test_atomic_fence() {
entry:
call void @llvm.nacl.atomic.fence(i32 6)
ret void
}
; CHECK-NEXT: define void @test_atomic_fence() {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @llvm.nacl.atomic.fence(i32 6)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define void @test_atomic_fence_all() {
entry:
call void @llvm.nacl.atomic.fence.all()
ret void
}
; CHECK-NEXT: define void @test_atomic_fence_all() {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @llvm.nacl.atomic.fence.all()
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define i32 @test_atomic_is_lock_free(i32 %iptr) {
entry:
%ptr = inttoptr i32 %iptr to i8*
%i = call i1 @llvm.nacl.atomic.is.lock.free(i32 4, i8* %ptr)
%r = zext i1 %i to i32
ret i32 %r
}
; CHECK-NEXT: define i32 @test_atomic_is_lock_free(i32 %iptr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %i = call i1 @llvm.nacl.atomic.is.lock.free(i32 4, i32 %iptr)
; CHECK-NEXT: %r = zext i1 %i to i32
; CHECK-NEXT: ret i32 %r
; CHECK-NEXT: }
; Tests that we don't get fooled by a fake NaCl intrinsic.
; RUN: llvm-as < %s | pnacl-freeze -allow-local-symbol-tables \
; RUN: | not %llvm2ice -notranslate -verbose=inst -build-on-read \
; RUN: -allow-pnacl-reader-error-recovery \
; RUN: -allow-local-symbol-tables \
; 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: (218:6) Invalid PNaCl intrinsic call to llvm.fake.i32
; This tests parsing NaCl intrinsics not related to atomic operations.
; RUN: llvm-as < %s | pnacl-freeze -allow-local-symbol-tables \
; RUN: | %llvm2ice -notranslate -verbose=inst -build-on-read \
; RUN: -allow-pnacl-reader-error-recovery \
; RUN: -allow-local-symbol-tables \
; RUN: | FileCheck %s
declare i8* @llvm.nacl.read.tp()
declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i32, i1)
declare void @llvm.memmove.p0i8.p0i8.i32(i8*, i8*, i32, i32, i1)
declare void @llvm.memset.p0i8.i32(i8*, i8, i32, i32, i1)
declare void @llvm.nacl.longjmp(i8*, i32)
declare i32 @llvm.nacl.setjmp(i8*)
declare float @llvm.sqrt.f32(float)
declare double @llvm.sqrt.f64(double)
declare void @llvm.trap()
declare i16 @llvm.bswap.i16(i16)
declare i32 @llvm.bswap.i32(i32)
declare i64 @llvm.bswap.i64(i64)
declare i32 @llvm.ctlz.i32(i32, i1)
declare i64 @llvm.ctlz.i64(i64, i1)
declare i32 @llvm.cttz.i32(i32, i1)
declare i64 @llvm.cttz.i64(i64, i1)
declare i32 @llvm.ctpop.i32(i32)
declare i64 @llvm.ctpop.i64(i64)
declare i8* @llvm.stacksave()
declare void @llvm.stackrestore(i8*)
define i32 @test_nacl_read_tp() {
entry:
%ptr = call i8* @llvm.nacl.read.tp()
%__1 = ptrtoint i8* %ptr to i32
ret i32 %__1
}
; CHECK: define i32 @test_nacl_read_tp() {
; CHECK-NEXT: entry:
; CHECK-NEXT: %ptr = call i32 @llvm.nacl.read.tp()
; CHECK-NEXT: ret i32 %ptr
; CHECK-NEXT: }
define void @test_memcpy(i32 %iptr_dst, i32 %iptr_src, i32 %len) {
entry:
%dst = inttoptr i32 %iptr_dst to i8*
%src = inttoptr i32 %iptr_src to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %src,
i32 %len, i32 1, i1 false)
ret void
}
; CHECK-NEXT: define void @test_memcpy(i32 %iptr_dst, i32 %iptr_src, i32 %len) {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i32 %iptr_dst, i32 %iptr_src, i32 %len, i32 1, i1 false)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define void @test_memmove(i32 %iptr_dst, i32 %iptr_src, i32 %len) {
entry:
%dst = inttoptr i32 %iptr_dst to i8*
%src = inttoptr i32 %iptr_src to i8*
call void @llvm.memmove.p0i8.p0i8.i32(i8* %dst, i8* %src,
i32 %len, i32 1, i1 false)
ret void
}
; CHECK-NEXT: define void @test_memmove(i32 %iptr_dst, i32 %iptr_src, i32 %len) {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @llvm.memmove.p0i8.p0i8.i32(i32 %iptr_dst, i32 %iptr_src, i32 %len, i32 1, i1 false)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define void @test_memset(i32 %iptr_dst, i32 %wide_val, i32 %len) {
entry:
%val = trunc i32 %wide_val to i8
%dst = inttoptr i32 %iptr_dst to i8*
call void @llvm.memset.p0i8.i32(i8* %dst, i8 %val,
i32 %len, i32 1, i1 false)
ret void
}
; CHECK-NEXT: define void @test_memset(i32 %iptr_dst, i32 %wide_val, i32 %len) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %val = trunc i32 %wide_val to i8
; CHECK-NEXT: call void @llvm.memset.p0i8.i32(i32 %iptr_dst, i8 %val, i32 %len, i32 1, i1 false)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define i32 @test_setjmplongjmp(i32 %iptr_env) {
entry:
%env = inttoptr i32 %iptr_env to i8*
%i = call i32 @llvm.nacl.setjmp(i8* %env)
%r1 = icmp eq i32 %i, 0
br i1 %r1, label %Zero, label %NonZero
Zero:
; Redundant inttoptr, to make --pnacl cast-eliding/re-insertion happy.
%env2 = inttoptr i32 %iptr_env to i8*
call void @llvm.nacl.longjmp(i8* %env2, i32 1)
ret i32 0
NonZero:
ret i32 1
}
; CHECK-NEXT: define i32 @test_setjmplongjmp(i32 %iptr_env) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %i = call i32 @llvm.nacl.setjmp(i32 %iptr_env)
; CHECK-NEXT: %r1 = icmp eq i32 %i, 0
; CHECK-NEXT: br i1 %r1, label %Zero, label %NonZero
; CHECK-NEXT: Zero:
; CHECK-NEXT: call void @llvm.nacl.longjmp(i32 %iptr_env, i32 1)
; CHECK-NEXT: ret i32 0
; CHECK-NEXT: NonZero:
; CHECK-NEXT: ret i32 1
; CHECK-NEXT: }
define float @test_sqrt_float(float %x, i32 %iptr) {
entry:
%r = call float @llvm.sqrt.f32(float %x)
%r2 = call float @llvm.sqrt.f32(float %r)
%r3 = call float @llvm.sqrt.f32(float -0.0)
%r4 = fadd float %r2, %r3
ret float %r4
}
; CHECK-NEXT: define float @test_sqrt_float(float %x, i32 %iptr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call float @llvm.sqrt.f32(float %x)
; CHECK-NEXT: %r2 = call float @llvm.sqrt.f32(float %r)
; CHECK-NEXT: %r3 = call float @llvm.sqrt.f32(float -0.000000e+00)
; CHECK-NEXT: %r4 = fadd float %r2, %r3
; CHECK-NEXT: ret float %r4
; CHECK-NEXT: }
define double @test_sqrt_double(double %x, i32 %iptr) {
entry:
%r = call double @llvm.sqrt.f64(double %x)
%r2 = call double @llvm.sqrt.f64(double %r)
%r3 = call double @llvm.sqrt.f64(double -0.0)
%r4 = fadd double %r2, %r3
ret double %r4
}
; CHECK-NEXT: define double @test_sqrt_double(double %x, i32 %iptr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call double @llvm.sqrt.f64(double %x)
; CHECK-NEXT: %r2 = call double @llvm.sqrt.f64(double %r)
; CHECK-NEXT: %r3 = call double @llvm.sqrt.f64(double -0.000000e+00)
; CHECK-NEXT: %r4 = fadd double %r2, %r3
; CHECK-NEXT: ret double %r4
; CHECK-NEXT: }
define i32 @test_trap(i32 %br) {
entry:
%r1 = icmp eq i32 %br, 0
br i1 %r1, label %Zero, label %NonZero
Zero:
call void @llvm.trap()
unreachable
NonZero:
ret i32 1
}
; CHECK-NEXT: define i32 @test_trap(i32 %br) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r1 = icmp eq i32 %br, 0
; CHECK-NEXT: br i1 %r1, label %Zero, label %NonZero
; CHECK-NEXT: Zero:
; CHECK-NEXT: call void @llvm.trap()
; CHECK-NEXT: unreachable
; CHECK-NEXT: NonZero:
; CHECK-NEXT: ret i32 1
; CHECK-NEXT: }
define i32 @test_bswap_16(i32 %x) {
entry:
%x_trunc = trunc i32 %x to i16
%r = call i16 @llvm.bswap.i16(i16 %x_trunc)
%r_zext = zext i16 %r to i32
ret i32 %r_zext
}
; CHECK-NEXT: define i32 @test_bswap_16(i32 %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %x_trunc = trunc i32 %x to i16
; CHECK-NEXT: %r = call i16 @llvm.bswap.i16(i16 %x_trunc)
; CHECK-NEXT: %r_zext = zext i16 %r to i32
; CHECK-NEXT: ret i32 %r_zext
; CHECK-NEXT: }
define i32 @test_bswap_32(i32 %x) {
entry:
%r = call i32 @llvm.bswap.i32(i32 %x)
ret i32 %r
}
; CHECK-NEXT: define i32 @test_bswap_32(i32 %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i32 @llvm.bswap.i32(i32 %x)
; CHECK-NEXT: ret i32 %r
; CHECK-NEXT: }
define i64 @test_bswap_64(i64 %x) {
entry:
%r = call i64 @llvm.bswap.i64(i64 %x)
ret i64 %r
}
; CHECK-NEXT: define i64 @test_bswap_64(i64 %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i64 @llvm.bswap.i64(i64 %x)
; CHECK-NEXT: ret i64 %r
; CHECK-NEXT: }
define i32 @test_ctlz_32(i32 %x) {
entry:
%r = call i32 @llvm.ctlz.i32(i32 %x, i1 false)
ret i32 %r
}
; CHECK-NEXT: define i32 @test_ctlz_32(i32 %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i32 @llvm.ctlz.i32(i32 %x, i1 false)
; CHECK-NEXT: ret i32 %r
; CHECK-NEXT: }
define i64 @test_ctlz_64(i64 %x) {
entry:
%r = call i64 @llvm.ctlz.i64(i64 %x, i1 false)
ret i64 %r
}
; CHECK-NEXT: define i64 @test_ctlz_64(i64 %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i64 @llvm.ctlz.i64(i64 %x, i1 false)
; CHECK-NEXT: ret i64 %r
; CHECK-NEXT: }
define i32 @test_cttz_32(i32 %x) {
entry:
%r = call i32 @llvm.cttz.i32(i32 %x, i1 false)
ret i32 %r
}
; CHECK-NEXT: define i32 @test_cttz_32(i32 %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i32 @llvm.cttz.i32(i32 %x, i1 false)
; CHECK-NEXT: ret i32 %r
; CHECK-NEXT: }
define i64 @test_cttz_64(i64 %x) {
entry:
%r = call i64 @llvm.cttz.i64(i64 %x, i1 false)
ret i64 %r
}
; CHECK-NEXT: define i64 @test_cttz_64(i64 %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i64 @llvm.cttz.i64(i64 %x, i1 false)
; CHECK-NEXT: ret i64 %r
; CHECK-NEXT: }
define i32 @test_popcount_32(i32 %x) {
entry:
%r = call i32 @llvm.ctpop.i32(i32 %x)
ret i32 %r
}
; CHECK-NEXT: define i32 @test_popcount_32(i32 %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i32 @llvm.ctpop.i32(i32 %x)
; CHECK-NEXT: ret i32 %r
; CHECK-NEXT: }
define i64 @test_popcount_64(i64 %x) {
entry:
%r = call i64 @llvm.ctpop.i64(i64 %x)
ret i64 %r
}
; CHECK-NEXT: define i64 @test_popcount_64(i64 %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %r = call i64 @llvm.ctpop.i64(i64 %x)
; CHECK-NEXT: ret i64 %r
; CHECK-NEXT: }
define void @test_stacksave_noalloca() {
entry:
%sp = call i8* @llvm.stacksave()
call void @llvm.stackrestore(i8* %sp)
ret void
}
; CHECK-NEXT: define void @test_stacksave_noalloca() {
; CHECK-NEXT: entry:
; CHECK-NEXT: %sp = call i32 @llvm.stacksave()
; CHECK-NEXT: call void @llvm.stackrestore(i32 %sp)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
declare i32 @foo(i32 %x)
define void @test_stacksave_multiple(i32 %x) {
entry:
%x_4 = mul i32 %x, 4
%sp1 = call i8* @llvm.stacksave()
%tmp1 = alloca i8, i32 %x_4, align 4
%sp2 = call i8* @llvm.stacksave()
%tmp2 = alloca i8, i32 %x_4, align 4
%y = call i32 @foo(i32 %x)
%sp3 = call i8* @llvm.stacksave()
%tmp3 = alloca i8, i32 %x_4, align 4
%__9 = bitcast i8* %tmp1 to i32*
store i32 %y, i32* %__9, align 1
%__10 = bitcast i8* %tmp2 to i32*
store i32 %x, i32* %__10, align 1
%__11 = bitcast i8* %tmp3 to i32*
store i32 %x, i32* %__11, align 1
call void @llvm.stackrestore(i8* %sp1)
ret void
}
; CHECK-NEXT: define void @test_stacksave_multiple(i32 %x) {
; CHECK-NEXT: entry:
; CHECK-NEXT: %x_4 = mul i32 %x, 4
; CHECK-NEXT: %sp1 = call i32 @llvm.stacksave()
; CHECK-NEXT: %tmp1 = alloca i8, i32 %x_4, align 4
; CHECK-NEXT: %sp2 = call i32 @llvm.stacksave()
; CHECK-NEXT: %tmp2 = alloca i8, i32 %x_4, align 4
; CHECK-NEXT: %y = call i32 @foo(i32 %x)
; CHECK-NEXT: %sp3 = call i32 @llvm.stacksave()
; CHECK-NEXT: %tmp3 = alloca i8, i32 %x_4, align 4
; CHECK-NEXT: store i32 %y, i32* %tmp1, align 1
; CHECK-NEXT: store i32 %x, i32* %tmp2, align 1
; CHECK-NEXT: store i32 %x, i32* %tmp3, align 1
; CHECK-NEXT: call void @llvm.stackrestore(i32 %sp1)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
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