diff options
author | Karl Schimpf <kschimpf@google.com> | 2013-08-28 14:34:19 -0700 |
---|---|---|
committer | Karl Schimpf <kschimpf@google.com> | 2013-08-28 14:34:19 -0700 |
commit | 493d4ab9edba2be18c916d80aaa100a0e51e1f51 (patch) | |
tree | 0fe55c21475362e19e6aec0404633f26c89f314e | |
parent | 117111856d5cca5a0286792180ad9e241ea4d701 (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.h | 8 | ||||
-rw-r--r-- | lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp | 99 | ||||
-rw-r--r-- | lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp | 63 | ||||
-rw-r--r-- | lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp | 19 | ||||
-rw-r--r-- | test/NaCl/Bitcode/bitcast-elide.ll | 1 | ||||
-rw-r--r-- | test/NaCl/Bitcode/call-elide.ll | 391 | ||||
-rw-r--r-- | test/NaCl/Bitcode/inttoptr-elide.ll | 1 | ||||
-rw-r--r-- | test/NaCl/Bitcode/ptrtoint-elide.ll | 10 | ||||
-rw-r--r-- | tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp | 1 |
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"; } |