aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Schimpf <kschimpf@google.com>2013-08-28 14:34:19 -0700
committerKarl Schimpf <kschimpf@google.com>2013-08-28 14:34:19 -0700
commit493d4ab9edba2be18c916d80aaa100a0e51e1f51 (patch)
tree0fe55c21475362e19e6aec0404633f26c89f314e
parent117111856d5cca5a0286792180ad9e241ea4d701 (diff)
Handle pointer conversions for call instructions.
This also should complete the changes associated with removing pointer cast instructions from the PNaCl bitcode file. BUG= https://code.google.com/p/nativeclient/issues/detail?id=3544 R=dschuff@chromium.org, jvoung@chromium.org Review URL: https://codereview.chromium.org/23482002
-rw-r--r--include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h8
-rw-r--r--lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp99
-rw-r--r--lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp63
-rw-r--r--lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp19
-rw-r--r--test/NaCl/Bitcode/bitcast-elide.ll1
-rw-r--r--test/NaCl/Bitcode/call-elide.ll391
-rw-r--r--test/NaCl/Bitcode/inttoptr-elide.ll1
-rw-r--r--test/NaCl/Bitcode/ptrtoint-elide.ll10
-rw-r--r--tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp1
9 files changed, 502 insertions, 91 deletions
diff --git a/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h b/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h
index ebe5a972e5..8361a17642 100644
--- a/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h
+++ b/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h
@@ -309,8 +309,8 @@ namespace naclbitc {
// 32 is unused.
FUNC_CODE_DEBUG_LOC_AGAIN = 33, // Not used in PNaCl.
- FUNC_CODE_INST_CALL = 34, // CALL: [cc, fnid, args...]
-
+ FUNC_CODE_INST_CALL = 34, // CALL: [cc, fnid, args...]
+ // See FUNC_CODE_INST_CALL_INDIRECT below.
FUNC_CODE_DEBUG_LOC = 35, // Not used in PNaCl.
FUNC_CODE_INST_FENCE = 36, // Not used in PNaCl.
FUNC_CODE_INST_CMPXCHG = 37, // Not used in PNaCl.
@@ -319,7 +319,9 @@ namespace naclbitc {
FUNC_CODE_INST_LANDINGPAD = 40, // Not used in PNaCl.
FUNC_CODE_INST_LOADATOMIC = 41, // Not used in PNaCl.
FUNC_CODE_INST_STOREATOMIC = 42, // Not used in PNaCl.
- FUNC_CODE_INST_FORWARDTYPEREF = 43 // TYPE: [opval, ty]
+ FUNC_CODE_INST_FORWARDTYPEREF = 43, // TYPE: [opval, ty]
+ // PNaCl version 2: CALL_INDIRECT: [cc, fnid, fnty, args...]
+ FUNC_CODE_INST_CALL_INDIRECT = 44
};
enum NaClUseListCodes {
diff --git a/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp b/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp
index fbe1fc0165..028e750d92 100644
--- a/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp
+++ b/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp
@@ -1315,16 +1315,15 @@ Value *NaClBitcodeReader::ConvertOpToScalar(Value *Op, unsigned BBIndex,
Value *NaClBitcodeReader::ConvertOpToType(Value *Op, Type *T,
unsigned BBIndex) {
- // Note: Currently only knows how to add inttoptr and bitcast type
- // conversions for non-phi nodes, since these are the only elided
- // instructions in the bitcode writer.
- //
- // TODO(kschimpf): Generalize this as we expand elided conversions.
Type *OpTy = Op->getType();
if (OpTy == T) return Op;
if (OpTy->isPointerTy()) {
- return CreateCast(BBIndex, Instruction::BitCast, T, Op);
+ if (T == IntPtrType) {
+ return ConvertOpToScalar(Op, BBIndex);
+ } else {
+ return CreateCast(BBIndex, Instruction::BitCast, T, Op);
+ }
} else if (OpTy == IntPtrType) {
return CreateCast(BBIndex, Instruction::IntToPtr, T, Op);
}
@@ -1687,7 +1686,7 @@ bool NaClBitcodeReader::ParseFunctionBody(Function *F) {
return Error("Invalid LOAD record");
switch (GetPNaClVersion()) {
case 1:
- I = new LoadInst(Op, "", Record[OpNum+1], (1 << Record[OpNum]) >> 1);
+ I = new LoadInst(Op, "", Record[OpNum+1], (1 << Record[OpNum]) >> 1);
break;
case 2: {
// Add pointer cast to op.
@@ -1696,8 +1695,8 @@ bool NaClBitcodeReader::ParseFunctionBody(Function *F) {
return Error("Invalid type for load instruction");
Op = ConvertOpToType(Op, T->getPointerTo(), CurBBNo);
if (Op == 0) return true;
- I = new LoadInst(Op, "", false, (1 << Record[OpNum]) >> 1);
- break;
+ I = new LoadInst(Op, "", false, (1 << Record[OpNum]) >> 1);
+ break;
}
}
break;
@@ -1712,28 +1711,29 @@ bool NaClBitcodeReader::ParseFunctionBody(Function *F) {
return Error("Invalid STORE record");
switch (GetPNaClVersion()) {
case 1:
- if (OpNum+2 != Record.size())
- return Error("Invalid STORE record");
- I = new StoreInst(Val, Ptr, Record[OpNum+1], (1 << Record[OpNum]) >> 1);
- break;
+ if (OpNum+2 != Record.size())
+ return Error("Invalid STORE record");
+ I = new StoreInst(Val, Ptr, Record[OpNum+1], (1 << Record[OpNum]) >> 1);
+ break;
case 2:
- if (OpNum+1 != Record.size())
- return Error("Invalid STORE record");
+ if (OpNum+1 != Record.size())
+ return Error("Invalid STORE record");
Val = ConvertOpToScalar(Val, CurBBNo);
Ptr = ConvertOpToType(Ptr, Val->getType()->getPointerTo(), CurBBNo);
- I = new StoreInst(Val, Ptr, false, (1 << Record[OpNum]) >> 1);
- break;
+ I = new StoreInst(Val, Ptr, false, (1 << Record[OpNum]) >> 1);
+ break;
}
break;
}
- case naclbitc::FUNC_CODE_INST_CALL: {
+ case naclbitc::FUNC_CODE_INST_CALL:
+ case naclbitc::FUNC_CODE_INST_CALL_INDIRECT: {
// CALL: [cc, fnid, arg0, arg1...]
- if (Record.size() < 2)
+ // PNaCl version 2: CALL_INDIRECT: [cc, fnid, fnty, args...]
+ if ((Record.size() < 2) ||
+ (BitCode == naclbitc::FUNC_CODE_INST_CALL_INDIRECT &&
+ Record.size() < 3))
return Error("Invalid CALL record");
- // TODO(kschimpf): Fix handling of type conversion to arguments for PNaCl,
- // to handle elided casts, once the bitcode writer knows how.
-
unsigned CCInfo = Record[0];
unsigned OpNum = 1;
@@ -1741,35 +1741,42 @@ bool NaClBitcodeReader::ParseFunctionBody(Function *F) {
if (popValue(Record, &OpNum, NextValueNo, &Callee))
return Error("Invalid CALL record");
- PointerType *OpTy = dyn_cast<PointerType>(Callee->getType());
+ // Build function type for call.
FunctionType *FTy = 0;
- if (OpTy) FTy = dyn_cast<FunctionType>(OpTy->getElementType());
- if (!FTy || Record.size() < FTy->getNumParams()+OpNum)
- return Error("Invalid CALL record");
-
- SmallVector<Value*, 16> Args;
- // Read the fixed params.
- for (unsigned i = 0, e = FTy->getNumParams(); i != e; ++i, ++OpNum) {
- if (FTy->getParamType(i)->isLabelTy())
- Args.push_back(getBasicBlock(Record[OpNum]));
- else
- Args.push_back(getValue(Record, OpNum, NextValueNo));
- if (Args.back() == 0) return Error("Invalid CALL record");
- }
-
- // Read type/value pairs for varargs params.
- if (!FTy->isVarArg()) {
- if (OpNum != Record.size())
- return Error("Invalid CALL record");
+ if (BitCode == naclbitc::FUNC_CODE_INST_CALL_INDIRECT) {
+ // Callee type has been elided, add back in.
+ Type *Type = getTypeByID(Record[2]);
+ ++OpNum;
+ if (FunctionType *FcnType = dyn_cast<FunctionType>(Type)) {
+ FTy = FcnType;
+ Callee = ConvertOpToType(Callee, FcnType->getPointerTo(), CurBBNo);
+ } else {
+ return Error("Invalid type for CALL_INDIRECT record");
+ }
} else {
- while (OpNum != Record.size()) {
- Value *Op;
- if (popValue(Record, &OpNum, NextValueNo, &Op))
- return Error("Invalid CALL record");
- Args.push_back(Op);
+ // Get type signature from callee.
+ if (PointerType *OpTy = dyn_cast<PointerType>(Callee->getType())) {
+ FTy = dyn_cast<FunctionType>(OpTy->getElementType());
}
+ if (FTy == 0)
+ return Error("Invalid type for CALL record");
+ }
+
+ unsigned NumParams = Record.size() - OpNum;
+ if (NumParams != FTy->getNumParams())
+ return Error("Invalid CALL record");
+
+ // Process call arguments.
+ SmallVector<Value*, 6> Args;
+ for (unsigned Index = 0; Index < NumParams; ++Index) {
+ Value *Arg;
+ if (popValue(Record, &OpNum, NextValueNo, &Arg))
+ Error("Invalid argument in CALL record");
+ Arg = ConvertOpToType(Arg, FTy->getParamType(Index), CurBBNo);
+ Args.push_back(Arg);
}
+ // Construct call.
I = CallInst::Create(Callee, Args);
cast<CallInst>(I)->setCallingConv(GetDecodedCallingConv(CCInfo>>1));
cast<CallInst>(I)->setTailCall(CCInfo & 1);
diff --git a/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp b/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp
index 6624ad1d7c..2ac88c92a4 100644
--- a/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp
+++ b/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp
@@ -975,32 +975,47 @@ static bool WriteInstruction(const Instruction &I, unsigned InstID,
}
break;
case Instruction::Call: {
- const CallInst &CI = cast<CallInst>(I);
- PointerType *PTy = cast<PointerType>(CI.getCalledValue()->getType());
- FunctionType *FTy = cast<FunctionType>(PTy->getElementType());
-
- Code = naclbitc::FUNC_CODE_INST_CALL;
-
- Vals.push_back((GetEncodedCallingConv(CI.getCallingConv()) << 1)
- | unsigned(CI.isTailCall()));
- pushValue(CI.getCalledValue(), InstID, Vals, VE, Stream); // Callee
-
- // Emit value #'s for the fixed parameters.
- for (unsigned i = 0, e = FTy->getNumParams(); i != e; ++i) {
- // Check for labels (can happen with asm labels).
- if (FTy->getParamType(i)->isLabelTy())
- Vals.push_back(VE.getValueID(CI.getArgOperand(i)));
- else
- // fixed param.
- pushValue(CI.getArgOperand(i), InstID, Vals, VE, Stream);
+ // CALL: [cc, fnid, args...]
+ // PNaCl version 2: CALL_INDIRECT: [cc, fnid, fnty, args...]
+
+ const CallInst &Call = cast<CallInst>(I);
+ const Value* Callee = Call.getCalledValue();
+ Vals.push_back((GetEncodedCallingConv(Call.getCallingConv()) << 1)
+ | unsigned(Call.isTailCall()));
+
+ pushValue(Callee, InstID, Vals, VE, Stream);
+
+ switch (PNaClVersion) {
+ case 1:
+ Code = naclbitc::FUNC_CODE_INST_CALL;
+ break;
+ case 2:
+ if (Callee == VE.ElideCasts(Callee)) {
+ // Since the call pointer has not been elided, we know that
+ // the call pointer has the type signature of the called
+ // function. This implies that the reader can use the type
+ // signature of the callee to figure out how to add casts to
+ // the arguments.
+ Code = naclbitc::FUNC_CODE_INST_CALL;
+ } else {
+ // If the cast was elided, a pointer conversion to a pointer
+ // was applied, meaning that this is an indirect call. For the
+ // reader, this implies that we can't use the type signature
+ // of the callee to resolve elided call arguments, since it is
+ // not known. Hence, we must send the type signature to the
+ // reader.
+ Code = naclbitc::FUNC_CODE_INST_CALL_INDIRECT;
+ PointerType *FcnPtrType =
+ cast<PointerType>(Callee->getType());
+ FunctionType *FcnType =
+ cast<FunctionType>(FcnPtrType->getElementType());
+ Vals.push_back(VE.getTypeID(FcnType));
+ }
+ break;
}
- // Emit type/value pairs for varargs params.
- if (FTy->isVarArg()) {
- for (unsigned i = FTy->getNumParams(), e = CI.getNumArgOperands();
- i != e; ++i)
- // varargs
- pushValue(CI.getArgOperand(i), InstID, Vals, VE, Stream);
+ for (unsigned I = 0, E = Call.getNumArgOperands(); I < E; ++I) {
+ pushValue(Call.getArgOperand(I), InstID, Vals, VE, Stream);
}
break;
}
diff --git a/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp b/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp
index 060a6d63f4..8cfdf13240 100644
--- a/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp
+++ b/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp
@@ -438,9 +438,6 @@ static bool ExpectsNormalizedPtr(const Value *V, const Instruction *Arg) {
const Instruction *I = dyn_cast<Instruction>(V);
if (I == 0) return false;
- // TODO(kschimpf) Expand this list to any operation that can handle
- // normalized pointers. That is loads and stores, function calls, and
- // instrinsic calls.
switch (I->getOpcode()) {
default:
return false;
@@ -448,6 +445,10 @@ static bool ExpectsNormalizedPtr(const Value *V, const Instruction *Arg) {
return I->getOperand(0) == Arg;
case Instruction::Store:
return I->getOperand(1) == Arg;
+ case Instruction::Call:
+ // For function calls, the function operand is normalized, and for
+ // intrinsic calls, all pointer arguments are normalized.
+ return true;
}
}
@@ -493,12 +494,13 @@ static bool ExpectsScalarValue(const Value *V, const Instruction *Arg) {
const SelectInst *Op = dyn_cast<SelectInst>(I);
return Arg == Op->getTrueValue() || Arg == Op->getFalseValue();
}
+ case Instruction::Call: {
+ // All operands (except the first, which must be a function pointer),
+ // can be scalar values.
+ const CallInst *Call = cast<CallInst>(I);
+ return Call->getCalledValue() != Arg;
+ }
}
- // TODO(kschimpf): Need to think more about how to handle following
- // instructions:
- // case Instruction::IntToPtr:
- // case Instruction::BitCast:
- // case Instruction::Call:
}
}
@@ -526,7 +528,6 @@ static inline bool IsInherentPtr(const Value *V) {
// llvm/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp.
const Value *NaClValueEnumerator::ElideCasts(const Value *V) {
if (PNaClVersion == 1) return V;
- // TODO(kschimpf): Expand this out to cover all cases.
if (const Instruction *I = dyn_cast<Instruction>(V)) {
switch (I->getOpcode()) {
default:
diff --git a/test/NaCl/Bitcode/bitcast-elide.ll b/test/NaCl/Bitcode/bitcast-elide.ll
index 383673d684..418b593762 100644
--- a/test/NaCl/Bitcode/bitcast-elide.ll
+++ b/test/NaCl/Bitcode/bitcast-elide.ll
@@ -1,5 +1,4 @@
; Test how we handle eliding (pointer) bitcast instructions.
-; TODO(kschimpf) Expand these tests as further CL's are added for issue 3544.
; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=1 \
; RUN: | pnacl-bcanalyzer -dump-records \
diff --git a/test/NaCl/Bitcode/call-elide.ll b/test/NaCl/Bitcode/call-elide.ll
new file mode 100644
index 0000000000..b3acb36a9d
--- /dev/null
+++ b/test/NaCl/Bitcode/call-elide.ll
@@ -0,0 +1,391 @@
+; Test how we handle eliding pointers in call instructions.
+
+; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=1 \
+; RUN: | pnacl-bcanalyzer -dump-records \
+; RUN: | FileCheck %s -check-prefix=PF1
+
+; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=1 | pnacl-thaw \
+; RUN: | llvm-dis - | FileCheck %s -check-prefix=TD1
+
+; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=2 \
+; RUN: | pnacl-bcanalyzer -dump-records \
+; RUN: | FileCheck %s -check-prefix=PF2
+
+; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=2 | pnacl-thaw \
+; RUN: | llvm-dis - | FileCheck %s -check-prefix=TD2
+
+; ------------------------------------------------------
+; Define some global functions/variables to be used in testing.
+
+
+@bytes = internal global [4 x i8] c"abcd"
+declare void @foo(i32 %i)
+declare i32 @bar(i32* %i)
+
+; ------------------------------------------------------
+; Test how we handle a direct call.
+
+define void @DirectCall() {
+ call void @foo(i32 0)
+ ret void
+}
+
+; TD1: define void @DirectCall() {
+; TD1-NEXT: call void @foo(i32 0)
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK>
+; PF1: </CONSTANTS_BLOCK>
+; PF1-NEXT: <INST_CALL op0=0 op1=14 op2=1/>
+; PF1-NEXT: <INST_RET/>
+; PF1-NEXT: </FUNCTION_BLOCK>
+
+; TD2: define void @DirectCall() {
+; TD2-NEXT: call void @foo(i32 0)
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK>
+; PF2: </CONSTANTS_BLOCK>
+; PF2-NEXT: <INST_CALL op0=0 op1=14 op2=1/>
+; PF2-NEXT: <INST_RET/>
+; PF2-NEXT: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+; Test how we handle a direct call with a normalized inttoptr argument.
+; Note: This code doesn't follow the PNaCl ABI in that function
+; calls can't get pointer arguments. However, intrinsic calls can, and
+; this code is a placeholder for such a test.
+
+define void @DirectCallIntToPtrArg(i32 %i) {
+ %1 = inttoptr i32 %i to i32*
+ %2 = call i32 @bar(i32* %1)
+ ret void
+}
+
+; TD1: define void @DirectCallIntToPtrArg(i32 %i) {
+; TD1-NEXT: %1 = inttoptr i32 %i to i32*
+; TD1-NEXT: %2 = call i32 @bar(i32* %1)
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST op0=1 op1=4 op2=10/>
+; PF1-NEXT: <INST_CALL op0=0 op1=14 op2=1/>
+; PF1-NEXT: <INST_RET/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define void @DirectCallIntToPtrArg(i32 %i) {
+; TD2-NEXT: %1 = inttoptr i32 %i to i32*
+; TD2-NEXT: %2 = call i32 @bar(i32* %1)
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_CALL op0=0 op1=13 op2=1/>
+; PF2-NEXT: <INST_RET/>
+; PF2: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+; Test how we handle a direct call with a normalized ptroint argument.
+
+define void @DirectCallPtrToIntArg() {
+ %1 = alloca i8, i32 4, align 8
+ %2 = ptrtoint i8* %1 to i32
+ call void @foo(i32 %2)
+ ret void
+}
+
+; TD1: define void @DirectCallPtrToIntArg() {
+; TD1-NEXT: %1 = alloca i8, i32 4, align 8
+; TD1-NEXT: %2 = ptrtoint i8* %1 to i32
+; TD1-NEXT: call void @foo(i32 %2)
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK>
+; PF1: </CONSTANTS_BLOCK>
+; PF1-NEXT: <INST_ALLOCA op0=1 op1=4/>
+; PF1-NEXT: <INST_CAST op0=1 op1=0 op2=9/>
+; PF1-NEXT: <INST_CALL op0=0 op1=16 op2=1/>
+; PF1-NEXT: <INST_RET/>
+; PF1-NEXT: </FUNCTION_BLOCK>
+
+; TD2: define void @DirectCallPtrToIntArg() {
+; TD2-NEXT: %1 = alloca i8, i32 4, align 8
+; TD2-NEXT: %2 = ptrtoint i8* %1 to i32
+; TD2-NEXT: call void @foo(i32 %2)
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK>
+; PF2: </CONSTANTS_BLOCK>
+; PF2-NEXT: <INST_ALLOCA op0=1 op1=4/>
+; PF2-NEXT: <INST_CALL op0=0 op1=15 op2=1/>
+; PF2-NEXT: <INST_RET/>
+; PF2-NEXT: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+; Test how we handle a direct call with a normalized bitcast argument.
+
+define void @DirectCallBitcastArg(i32 %i) {
+ %1 = bitcast [4 x i8]* @bytes to i32*
+ %2 = call i32 @bar(i32* %1)
+ ret void
+}
+
+; TD1: define void @DirectCallBitcastArg(i32 %i) {
+; TD1-NEXT: %1 = bitcast [4 x i8]* @bytes to i32*
+; TD1-NEXT: %2 = call i32 @bar(i32* %1)
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST op0=2 op1=4 op2=11/>
+; PF1-NEXT: <INST_CALL op0=0 op1=14 op2=1/>
+; PF1-NEXT: <INST_RET/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define void @DirectCallBitcastArg(i32 %i) {
+; TD2-NEXT: %1 = bitcast [4 x i8]* @bytes to i32*
+; TD2-NEXT: %2 = call i32 @bar(i32* %1)
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_CALL op0=0 op1=13 op2=2/>
+; PF2-NEXT: <INST_RET/>
+; PF2: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+; Test how we handle a direct call with a pointer to scalar conversion.
+
+define void @DirectCallScalarArg(i32* %ptr) {
+ %1 = ptrtoint [4 x i8]* @bytes to i32
+ call void @foo(i32 %1)
+ ret void
+}
+
+; TD1: define void @DirectCallScalarArg(i32* %ptr) {
+; TD1-NEXT: %1 = ptrtoint [4 x i8]* @bytes to i32
+; TD1-NEXT: call void @foo(i32 %1)
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST op0=2 op1=0 op2=9/>
+; PF1-NEXT: <INST_CALL op0=0 op1=15 op2=1/>
+; PF1-NEXT: <INST_RET/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define void @DirectCallScalarArg(i32* %ptr) {
+; TD2-NEXT: %1 = ptrtoint [4 x i8]* @bytes to i32
+; TD2-NEXT: call void @foo(i32 %1)
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_CALL op0=0 op1=14 op2=2/>
+; PF2-NEXT: <INST_RET/>
+; PF2: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+; Test how we handle an indirect call.
+
+define void @IndirectCall(i32 %i) {
+ %1 = inttoptr i32 %i to void (i32)*
+ call void %1(i32 %i)
+ ret void
+}
+
+; TD1: define void @IndirectCall(i32 %i) {
+; TD1-NEXT: %1 = inttoptr i32 %i to void (i32)*
+; TD1-NEXT: call void %1(i32 %i)
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST op0=1 op1=3 op2=10/>
+; PF1-NEXT: <INST_CALL op0=0 op1=1 op2=2/>
+; PF1-NEXT: <INST_RET/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define void @IndirectCall(i32 %i) {
+; TD2-NEXT: %1 = inttoptr i32 %i to void (i32)*
+; TD2-NEXT: call void %1(i32 %i)
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_CALL_INDIRECT op0=0 op1=1 op2=2 op3=1/>
+; PF2-NEXT: <INST_RET/>
+; PF2: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+; Test how we handle an indirect call with a normalized ptrtoint argument.
+
+define void @IndirectCallPtrToIntArg(i32 %i) {
+ %1 = alloca i8, i32 4, align 8
+ %2 = inttoptr i32 %i to void (i32)*
+ %3 = ptrtoint i8* %1 to i32
+ call void %2(i32 %3)
+ ret void
+}
+
+; TD1: define void @IndirectCallPtrToIntArg(i32 %i) {
+; TD1-NEXT: %1 = alloca i8, i32 4, align 8
+; TD1-NEXT: %2 = inttoptr i32 %i to void (i32)*
+; TD1-NEXT: %3 = ptrtoint i8* %1 to i32
+; TD1-NEXT: call void %2(i32 %3)
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK>
+; PF1: </CONSTANTS_BLOCK>
+; PF1-NEXT: <INST_ALLOCA op0=1 op1=4/>
+; PF1-NEXT: <INST_CAST op0=3 op1=3 op2=10/>
+; PF1-NEXT: <INST_CAST op0=2 op1=0 op2=9/>
+; PF1-NEXT: <INST_CALL op0=0 op1=2 op2=1/>
+; PF1-NEXT: <INST_RET/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define void @IndirectCallPtrToIntArg(i32 %i) {
+; TD2-NEXT: %1 = alloca i8, i32 4, align 8
+; TD2-NEXT: %2 = inttoptr i32 %i to void (i32)*
+; TD2-NEXT: %3 = ptrtoint i8* %1 to i32
+; TD2-NEXT: call void %2(i32 %3)
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK>
+; PF2: </CONSTANTS_BLOCK>
+; PF2-NEXT: <INST_ALLOCA op0=1 op1=4/>
+; PF2-NEXT: <INST_CALL_INDIRECT op0=0 op1=3 op2=2 op3=1/>
+; PF2-NEXT: <INST_RET/>
+; PF2: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+; Test how we handle a direct call with a normalized inttoptr argument.
+; Note: This code doesn't follow the PNaCl ABI in that function
+; calls can't get pointer arguments. However, intrinsic calls can, and
+; this code is a placeholder for such a test.
+
+define void @IndirectCallIntToPtrArg(i32 %i) {
+ %1 = inttoptr i32 %i to i32 (i32*)*
+ %2 = inttoptr i32 %i to i32*
+ %3 = call i32 %1(i32* %2)
+ ret void
+}
+
+; TD1: define void @IndirectCallIntToPtrArg(i32 %i) {
+; TD1-NEXT: %1 = inttoptr i32 %i to i32 (i32*)*
+; TD1-NEXT: %2 = inttoptr i32 %i to i32*
+; TD1-NEXT: %3 = call i32 %1(i32* %2)
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST op0=1 op1=6 op2=10/>
+; PF1-NEXT: <INST_CAST op0=2 op1=4 op2=10/>
+; PF1-NEXT: <INST_CALL op0=0 op1=2 op2=1/>
+; PF1-NEXT: <INST_RET/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define void @IndirectCallIntToPtrArg(i32 %i) {
+; TD2-NEXT: %1 = inttoptr i32 %i to i32 (i32*)*
+; TD2-NEXT: %2 = inttoptr i32 %i to i32*
+; TD2-NEXT: %3 = call i32 %1(i32* %2)
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_CALL_INDIRECT op0=0 op1=1 op2=5 op3=1/>
+; PF2-NEXT: <INST_RET/>
+; PF2: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+; Test how we handle an indirect call with a normalized bitcast argument.
+
+define void @IndirectCallBitcastArg(i32 %i) {
+ %1 = inttoptr i32 %i to i32 (i32*)*
+ %2 = bitcast [4 x i8]* @bytes to i32*
+ %3 = call i32 %1(i32* %2)
+ ret void
+}
+
+; TD1: define void @IndirectCallBitcastArg(i32 %i) {
+; TD1-NEXT: %1 = inttoptr i32 %i to i32 (i32*)*
+; TD1-NEXT: %2 = bitcast [4 x i8]* @bytes to i32*
+; TD1-NEXT: %3 = call i32 %1(i32* %2)
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST op0=1 op1=6 op2=10/>
+; PF1-NEXT: <INST_CAST op0=3 op1=4 op2=11/>
+; PF1-NEXT: <INST_CALL op0=0 op1=2 op2=1/>
+; PF1-NEXT: <INST_RET/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define void @IndirectCallBitcastArg(i32 %i) {
+; TD2-NEXT: %1 = inttoptr i32 %i to i32 (i32*)*
+; TD2-NEXT: %2 = bitcast [4 x i8]* @bytes to i32*
+; TD2-NEXT: %3 = call i32 %1(i32* %2)
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_CALL_INDIRECT op0=0 op1=1 op2=5 op3=2/>
+; PF2-NEXT: <INST_RET/>
+; PF2: </FUNCTION_BLOCK>
+
+; ------------------------------------------------------
+; Test how we handle an indirect call with a pointer to scalar conversion.
+
+define void @IndirectCallScalarArg(i32 %i, i32* %ptr) {
+ %1 = inttoptr i32 %i to void (i32)*
+ %2 = ptrtoint [4 x i8]* @bytes to i32
+ call void %1(i32 %2)
+ ret void
+}
+
+; TD1: define void @IndirectCallScalarArg(i32 %i, i32* %ptr) {
+; TD1-NEXT: %1 = inttoptr i32 %i to void (i32)*
+; TD1-NEXT: %2 = ptrtoint [4 x i8]* @bytes to i32
+; TD1-NEXT: call void %1(i32 %2)
+; TD1-NEXT: ret void
+; TD1-NEXT: }
+
+; PF1: <FUNCTION_BLOCK>
+; PF1-NEXT: <DECLAREBLOCKS op0=1/>
+; PF1-NEXT: <INST_CAST op0=2 op1=3 op2=10/>
+; PF1-NEXT: <INST_CAST op0=4 op1=0 op2=9/>
+; PF1-NEXT: <INST_CALL op0=0 op1=2 op2=1/>
+; PF1-NEXT: <INST_RET/>
+; PF1: </FUNCTION_BLOCK>
+
+; TD2: define void @IndirectCallScalarArg(i32 %i, i32* %ptr) {
+; TD2-NEXT: %1 = inttoptr i32 %i to void (i32)*
+; TD2-NEXT: %2 = ptrtoint [4 x i8]* @bytes to i32
+; TD2-NEXT: call void %1(i32 %2)
+; TD2-NEXT: ret void
+; TD2-NEXT: }
+
+; PF2: <FUNCTION_BLOCK>
+; PF2-NEXT: <DECLAREBLOCKS op0=1/>
+; PF2-NEXT: <INST_CALL_INDIRECT op0=0 op1=2 op2=2 op3=3/>
+; PF2-NEXT: <INST_RET/>
+; PF2: </FUNCTION_BLOCK>
diff --git a/test/NaCl/Bitcode/inttoptr-elide.ll b/test/NaCl/Bitcode/inttoptr-elide.ll
index 679f5f1d47..a01aaae1f7 100644
--- a/test/NaCl/Bitcode/inttoptr-elide.ll
+++ b/test/NaCl/Bitcode/inttoptr-elide.ll
@@ -1,5 +1,4 @@
; Test how we handle eliding inttoptr instructions.
-; TODO(kschimpf) Expand these tests as further CL's are added for issue 3544.
; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=1 \
; RUN: | pnacl-bcanalyzer -dump-records \
diff --git a/test/NaCl/Bitcode/ptrtoint-elide.ll b/test/NaCl/Bitcode/ptrtoint-elide.ll
index 43a82a0802..eba451c289 100644
--- a/test/NaCl/Bitcode/ptrtoint-elide.ll
+++ b/test/NaCl/Bitcode/ptrtoint-elide.ll
@@ -1,5 +1,4 @@
; Test how we handle eliding ptrtoint instructions.
-; TODO(kschimpf) Expand these tests as further CL's are added for issue 3544.
; RUN: llvm-as < %s | pnacl-freeze --pnacl-version=1 \
; RUN: | pnacl-bcanalyzer -dump-records \
@@ -684,9 +683,7 @@ define void @TestCasts() {
; ------------------------------------------------------
-; Show that if a ptrtoint is used in something other than known scalar operations,
-; it gets copied to the bitcode file
-; TODO(kschimpf): Remove this once all scalar operations have been handled.
+; Show that we elide a ptrtoint cast for a call.
define void @TestSavedPtrToInt() {
%1 = alloca i8, i32 4, align 8
%2 = ptrtoint i8* %1 to i32
@@ -723,9 +720,8 @@ define void @TestSavedPtrToInt() {
; PF2: <FUNCTION_BLOCK>
; PF2: </CONSTANTS_BLOCK>
; PF2-NEXT: <INST_ALLOCA op0=2 op1=4/>
-; PF2-NEXT: <INST_CAST op0=1 op1=0 op2=9/>
-; PF2-NEXT: <INST_BINOP op0=1 op1=3 op2=0/>
-; PF2-NEXT: <INST_CALL op0=0 op1=26 op2=2/>
+; PF2-NEXT: <INST_BINOP op0=1 op1=2 op2=0/>
+; PF2-NEXT: <INST_CALL op0=0 op1=25 op2=2/>
; PF2-NEXT: <INST_RET/>
; PF2-NEXT: </FUNCTION_BLOCK>
diff --git a/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp b/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp
index 2e4ff2d162..73b328f118 100644
--- a/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp
+++ b/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp
@@ -248,6 +248,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
case naclbitc::FUNC_CODE_INST_VSELECT: return "INST_VSELECT";
case naclbitc::FUNC_CODE_DEBUG_LOC_AGAIN: return "DEBUG_LOC_AGAIN";
case naclbitc::FUNC_CODE_INST_CALL: return "INST_CALL";
+ case naclbitc::FUNC_CODE_INST_CALL_INDIRECT: return "INST_CALL_INDIRECT";
case naclbitc::FUNC_CODE_DEBUG_LOC: return "DEBUG_LOC";
case naclbitc::FUNC_CODE_INST_FORWARDTYPEREF: return "FORWARDTYPEREF";
}