Fix call instructions to check parameter types for consistency.

Checks each parameter of a call against the corresponding function
signature.  It also checks that parameters are valid PNaCl parameter
types, unless the call is to an intrinsic.

BUG= https://code.google.com/p/nativeclient/issues/detail?id=4309
R=stichnot@chromium.org

Review URL: https://codereview.chromium.org/1354673002 .
diff --git a/src/IceTypes.cpp b/src/IceTypes.cpp
index 6cc79b7..3756a10 100644
--- a/src/IceTypes.cpp
+++ b/src/IceTypes.cpp
@@ -37,7 +37,8 @@
 };
 // Define a temporary set of enum values based on ICETYPE_PROPS_TABLE
 enum {
-#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, CompareResult)  \
+#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, IsParam,        \
+          CompareResult)                                                       \
   _props_table_tag_##tag,
   ICETYPE_PROPS_TABLE
 #undef X
@@ -51,7 +52,8 @@
 ICETYPE_TABLE
 #undef X
 // Assert that tags in ICETYPE_PROPS_TABLE is in ICETYPE_TABLE.
-#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, CompareResult)  \
+#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, IsParam,        \
+          CompareResult)                                                       \
   static_assert(                                                               \
       (unsigned)_table_tag_##tag == (unsigned)_props_table_tag_##tag,          \
       "Inconsistency between ICETYPE_PROPS_TABLE and ICETYPE_TABLE");
@@ -69,13 +71,15 @@
 };
 // Define constants for boolean flag if vector in ICETYPE_PROPS_TABLE.
 enum {
-#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, CompareResult)  \
+#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, IsParam,        \
+          CompareResult)                                                       \
   _props_table_IsVec_##tag = IsVec,
   ICETYPE_PROPS_TABLE
 #undef X
 };
 // Verify that the number of vector elements is consistent with IsVec.
-#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, CompareResult)  \
+#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, IsParam,        \
+          CompareResult)                                                       \
   static_assert((_table_elts_##tag > 1) == _props_table_IsVec_##tag,           \
                 "Inconsistent vector specification in ICETYPE_PROPS_TABLE");
 ICETYPE_PROPS_TABLE
@@ -107,14 +111,16 @@
   bool TypeIsScalarFloatingType;
   bool TypeIsVectorFloatingType;
   bool TypeIsLoadStoreType;
+  bool TypeIsCallParameterType;
   Type CompareResultType;
 };
 
 const TypePropertyFields TypePropertiesTable[] = {
-#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, CompareResult)  \
+#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, IsParam,        \
+          CompareResult)                                                       \
   {                                                                            \
     IsVec, IsInt, IsInt & !IsVec, IsInt & IsVec, IsIntArith, IsFloat,          \
-        IsFloat & !IsVec, IsFloat & IsVec, IsLoadStore, CompareResult          \
+        IsFloat & !IsVec, IsFloat & IsVec, IsLoadStore, IsParam, CompareResult \
   }                                                                            \
   ,
     ICETYPE_PROPS_TABLE
@@ -240,6 +246,14 @@
   return false;
 }
 
+bool isCallParameterType(Type Ty) {
+  size_t Index = static_cast<size_t>(Ty);
+  if (Index < IceType_NUM)
+    return TypePropertiesTable[Index].TypeIsCallParameterType;
+  llvm_unreachable("Invalid type for isCallParameterType()");
+  return false;
+}
+
 Type getCompareResultType(Type Ty) {
   size_t Index = static_cast<size_t>(Ty);
   if (Index < IceType_NUM)
diff --git a/src/IceTypes.def b/src/IceTypes.def
index b86dba8..8db8c63 100644
--- a/src/IceTypes.def
+++ b/src/IceTypes.def
@@ -19,33 +19,33 @@
 // sandboxes, but for now the 64-bit architectures use ELF64:
 // https://code.google.com/p/nativeclient/issues/detail?id=349 TODO: Whoever
 // adds AArch64 will need to set ABI e_flags.
-#define TARGETARCH_TABLE                                                 \
-  /* enum value, printable string, is_elf64,   e_machine, e_flags */     \
-  X(Target_X8632, "x86-32", false, EM_386,     0)                        \
-  X(Target_X8664, "x86-64", true,  EM_X86_64,  0)                        \
-  X(Target_ARM32, "arm32",  false, EM_ARM,     EF_ARM_EABI_VER5)         \
-  X(Target_ARM64, "arm64",  true,  EM_AARCH64, 0)                        \
-  X(Target_MIPS32,"mips32", false, EM_MIPS,    0)                        \
-  //#define X(tag, str, is_elf64, e_machine, e_flags)
+#define TARGETARCH_TABLE                                                       \
+  /* enum value, printable string, is_elf64,   e_machine, e_flags */           \
+  X(Target_X8632, "x86-32", false, EM_386,     0)                              \
+  X(Target_X8664, "x86-64", true,  EM_X86_64,  0)                              \
+  X(Target_ARM32, "arm32",  false, EM_ARM,     EF_ARM_EABI_VER5)               \
+  X(Target_ARM64, "arm64",  true,  EM_AARCH64, 0)                              \
+  X(Target_MIPS32,"mips32", false, EM_MIPS,    0)                              \
+//#define X(tag, str, is_elf64, e_machine, e_flags)
 
-#define ICETYPE_TABLE                                                    \
-  /* enum value, log_2(size), align, # elts, element type, printable */  \
-  /*     string (size and alignment in bytes) */                         \
-  X(IceType_void,  -1,  0,     1,      IceType_void, "void")             \
-  X(IceType_i1,     0,  1,     1,      IceType_i1,   "i1")               \
-  X(IceType_i8,     0,  1,     1,      IceType_i8,   "i8")               \
-  X(IceType_i16,    1,  1,     1,      IceType_i16,  "i16")              \
-  X(IceType_i32,    2,  1,     1,      IceType_i32,  "i32")              \
-  X(IceType_i64,    3,  1,     1,      IceType_i64,  "i64")              \
-  X(IceType_f32,    2,  4,     1,      IceType_f32,  "float")            \
-  X(IceType_f64,    3,  8,     1,      IceType_f64,  "double")           \
-  X(IceType_v4i1,   4,  1,     4,      IceType_i1,   "<4 x i1>")         \
-  X(IceType_v8i1,   4,  1,     8,      IceType_i1,   "<8 x i1>")         \
-  X(IceType_v16i1,  4,  1,    16,      IceType_i1,   "<16 x i1>")        \
-  X(IceType_v16i8,  4,  1,    16,      IceType_i8,   "<16 x i8>")        \
-  X(IceType_v8i16,  4,  2,     8,      IceType_i16,  "<8 x i16>")        \
-  X(IceType_v4i32,  4,  4,     4,      IceType_i32,  "<4 x i32>")        \
-  X(IceType_v4f32,  4,  4,     4,      IceType_f32,  "<4 x float>")      \
+#define ICETYPE_TABLE                                                          \
+  /* enum value, log_2(size), align, # elts, element type, printable */        \
+  /*     string (size and alignment in bytes) */                               \
+  X(IceType_void,  -1,  0,     1,      IceType_void, "void")                   \
+  X(IceType_i1,     0,  1,     1,      IceType_i1,   "i1")                     \
+  X(IceType_i8,     0,  1,     1,      IceType_i8,   "i8")                     \
+  X(IceType_i16,    1,  1,     1,      IceType_i16,  "i16")                    \
+  X(IceType_i32,    2,  1,     1,      IceType_i32,  "i32")                    \
+  X(IceType_i64,    3,  1,     1,      IceType_i64,  "i64")                    \
+  X(IceType_f32,    2,  4,     1,      IceType_f32,  "float")                  \
+  X(IceType_f64,    3,  8,     1,      IceType_f64,  "double")                 \
+  X(IceType_v4i1,   4,  1,     4,      IceType_i1,   "<4 x i1>")               \
+  X(IceType_v8i1,   4,  1,     8,      IceType_i1,   "<8 x i1>")               \
+  X(IceType_v16i1,  4,  1,    16,      IceType_i1,   "<16 x i1>")              \
+  X(IceType_v16i8,  4,  1,    16,      IceType_i8,   "<16 x i8>")              \
+  X(IceType_v8i16,  4,  2,     8,      IceType_i16,  "<8 x i16>")              \
+  X(IceType_v4i32,  4,  4,     4,      IceType_i32,  "<4 x i32>")              \
+  X(IceType_v4f32,  4,  4,     4,      IceType_f32,  "<4 x float>")            \
 //#define X(tag, sizeLog2, align, elts, elty, str)
 
 // Dictionary:
@@ -54,25 +54,27 @@
 //   F - Is floating point value (scalar or vector).
 //   IA - Is integer arithmetic type
 //   LS - true if load/store allowed on type.
+//   P  - true if can be used for parameter of call.
 //   CR - Result type of compare instruction for argument type
 //        (IceType_void if disallowed)
-#define ICETYPE_PROPS_TABLE                                              \
-  /* Enum Value    V  I  F IA  LS CR */                                  \
-  X(IceType_void,  0, 0, 0, 0, 0, IceType_void)                          \
-  X(IceType_i1,    0, 1, 0, 0, 0, IceType_i1)                            \
-  X(IceType_i8,    0, 1, 0, 1, 1, IceType_i1)                            \
-  X(IceType_i16,   0, 1, 0, 1, 1, IceType_i1)                            \
-  X(IceType_i32,   0, 1, 0, 1, 1, IceType_i1)                            \
-  X(IceType_i64,   0, 1, 0, 1, 1, IceType_i1)                            \
-  X(IceType_f32,   0, 0, 1, 0, 1, IceType_i1)                            \
-  X(IceType_f64,   0, 0, 1, 0, 1, IceType_i1)                            \
-  X(IceType_v4i1,  1, 1, 0, 0, 0, IceType_v4i1)                          \
-  X(IceType_v8i1,  1, 1, 0, 0, 0, IceType_v8i1)                          \
-  X(IceType_v16i1, 1, 1, 0, 0, 0, IceType_v16i1)                         \
-  X(IceType_v16i8, 1, 1, 0, 1, 1, IceType_v16i1)                         \
-  X(IceType_v8i16, 1, 1, 0, 1, 1, IceType_v8i1)                          \
-  X(IceType_v4i32, 1, 1, 0, 1, 1, IceType_v4i1)                          \
-  X(IceType_v4f32, 1, 0, 1, 0, 1, IceType_v4i1)                          \
-//#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, CompareResult)
+#define ICETYPE_PROPS_TABLE                                                    \
+  /* Enum Value    V  I  F IA  LS P CR */                                      \
+  X(IceType_void,  0, 0, 0, 0, 0, 0, IceType_void)                             \
+  X(IceType_i1,    0, 1, 0, 0, 0, 0, IceType_i1)                               \
+  X(IceType_i8,    0, 1, 0, 1, 1, 0, IceType_i1)                               \
+  X(IceType_i16,   0, 1, 0, 1, 1, 0, IceType_i1)                               \
+  X(IceType_i32,   0, 1, 0, 1, 1, 1, IceType_i1)                               \
+  X(IceType_i64,   0, 1, 0, 1, 1, 1, IceType_i1)                               \
+  X(IceType_f32,   0, 0, 1, 0, 1, 1, IceType_i1)                               \
+  X(IceType_f64,   0, 0, 1, 0, 1, 1, IceType_i1)                               \
+  X(IceType_v4i1,  1, 1, 0, 0, 0, 1, IceType_v4i1)                             \
+  X(IceType_v8i1,  1, 1, 0, 0, 0, 1, IceType_v8i1)                             \
+  X(IceType_v16i1, 1, 1, 0, 0, 0, 1, IceType_v16i1)                            \
+  X(IceType_v16i8, 1, 1, 0, 1, 1, 1, IceType_v16i1)                            \
+  X(IceType_v8i16, 1, 1, 0, 1, 1, 1, IceType_v8i1)                             \
+  X(IceType_v4i32, 1, 1, 0, 1, 1, 1, IceType_v4i1)                             \
+  X(IceType_v4f32, 1, 0, 1, 0, 1, 1, IceType_v4i1)                             \
+//#define X(tag, IsVec, IsInt, IsFloat, IsIntArith, IsLoadStore, IsParam,      \
+//          CompareResult)
 
 #endif // SUBZERO_SRC_ICETYPES_DEF
diff --git a/src/IceTypes.h b/src/IceTypes.h
index f176e9b..f6705e9 100644
--- a/src/IceTypes.h
+++ b/src/IceTypes.h
@@ -81,6 +81,14 @@
 /// Returns true if the given type can be used in a load instruction.
 bool isLoadStoreType(Type Ty);
 
+/// Returns true if the given type can be used as a parameter type in a call.
+bool isCallParameterType(Type Ty);
+
+/// Returns true if the given type can be used as the return type of a call.
+inline bool isCallReturnType(Type Ty) {
+  return Ty == IceType_void || isCallParameterType(Ty);
+}
+
 /// Returns type generated by applying the compare instructions (icmp and fcmp)
 /// to arguments of the given type. Returns IceType_void if compare is not
 /// allowed.
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp
index d7678f3..ac9698e 100644
--- a/src/PNaClTranslator.cpp
+++ b/src/PNaClTranslator.cpp
@@ -2644,12 +2644,13 @@
     // Extract out the called function and its return type.
     uint32_t CalleeIndex = convertRelativeToAbsIndex(Values[1], BaseIndex);
     Ice::Operand *Callee = getOperand(CalleeIndex);
+    const Ice::FuncSigType *Signature = nullptr;
     Ice::Type ReturnType = Ice::IceType_void;
     const Ice::Intrinsics::FullIntrinsicInfo *IntrinsicInfo = nullptr;
     if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) {
       Ice::FunctionDeclaration *Fcn = Context->getFunctionByID(CalleeIndex);
-      const Ice::FuncSigType &Signature = Fcn->getSignature();
-      ReturnType = Signature.getReturnType();
+      Signature = &Fcn->getSignature();
+      ReturnType = Signature->getReturnType();
 
       // Check if this direct call is to an Intrinsic (starts with "llvm.")
       bool BadIntrinsic;
@@ -2661,13 +2662,23 @@
         raw_string_ostream StrBuf(Buffer);
         StrBuf << "Invalid PNaCl intrinsic call to " << Name;
         Error(StrBuf.str());
-        appendErrorInstruction(ReturnType);
+        if (ReturnType != Ice::IceType_void)
+          appendErrorInstruction(ReturnType);
         return;
       }
     } else {
       ReturnType = Context->getSimpleTypeByID(Values[2]);
     }
 
+    // Check return type.
+    if (IntrinsicInfo == nullptr && !isCallReturnType(ReturnType)) {
+      std::string Buffer;
+      raw_string_ostream StrBuf(Buffer);
+      StrBuf << "Return type of called function is invalid: " << ReturnType;
+      Error(StrBuf.str());
+      ReturnType = Ice::IceType_i32;
+    }
+
     // Extract call information.
     uint64_t CCInfo = Values[0];
     CallingConv::ID CallingConv;
@@ -2677,12 +2688,21 @@
       StrBuf << "Function call calling convention value " << (CCInfo >> 1)
              << " not understood.";
       Error(StrBuf.str());
-      appendErrorInstruction(ReturnType);
+      if (ReturnType != Ice::IceType_void)
+        appendErrorInstruction(ReturnType);
       return;
     }
     bool IsTailCall = static_cast<bool>(CCInfo & 1);
     Ice::SizeT NumParams = Values.size() - ParamsStartIndex;
-
+    if (Signature && NumParams != Signature->getNumArgs()) {
+      std::string Buffer;
+      raw_string_ostream StrBuf(Buffer);
+      StrBuf << "Call has " << NumParams
+             << " parameters. Signature expects: " << Signature->getNumArgs();
+      Error(StrBuf.str());
+      // Error recover by only checking parameters in both signature and call.
+      NumParams = std::min(NumParams, Signature->getNumArgs());
+    }
     if (isIRGenerationDisabled()) {
       assert(Callee == nullptr);
       // Check that parameters are defined.
@@ -2695,30 +2715,67 @@
         setNextLocalInstIndex(nullptr);
       return;
     }
-
     // Create the call instruction.
     Ice::Variable *Dest = (ReturnType == Ice::IceType_void)
                               ? nullptr
                               : getNextInstVar(ReturnType);
-    Ice::InstCall *Inst = nullptr;
+    std::unique_ptr<Ice::InstCall> Inst;
     if (IntrinsicInfo) {
-      Inst = Ice::InstIntrinsicCall::create(Func.get(), NumParams, Dest, Callee,
-                                            IntrinsicInfo->Info);
+      Inst.reset(Ice::InstIntrinsicCall::create(Func.get(), NumParams, Dest,
+                                                Callee, IntrinsicInfo->Info));
     } else {
-      Inst = Ice::InstCall::create(Func.get(), NumParams, Dest, Callee,
-                                   IsTailCall);
+      Inst.reset(Ice::InstCall::create(Func.get(), NumParams, Dest, Callee,
+                                       IsTailCall));
     }
 
     // Add parameters.
     for (Ice::SizeT ParamIndex = 0; ParamIndex < NumParams; ++ParamIndex) {
-      Inst->addArg(
-          getRelativeOperand(Values[ParamsStartIndex + ParamIndex], BaseIndex));
+      Ice::Operand *Op =
+          getRelativeOperand(Values[ParamsStartIndex + ParamIndex], BaseIndex);
+      if (Op == nullptr) {
+        std::string Buffer;
+        raw_string_ostream StrBuf(Buffer);
+        StrBuf << "Parameter " << ParamIndex << " of call not defined";
+        Error(StrBuf.str());
+        if (ReturnType != Ice::IceType_void)
+          appendErrorInstruction(ReturnType);
+        return;
+      }
+
+      // Check that parameter type is valid.
+      if (Signature) {
+        if (Op->getType() != Signature->getArgType(ParamIndex)) {
+          std::string Buffer;
+          raw_string_ostream StrBuf(Buffer);
+          StrBuf << "Call argument " << *Op << " expects "
+                 << Signature->getArgType(ParamIndex)
+                 << ". Found: " << Op->getType();
+          Error(StrBuf.str());
+        } else if (IntrinsicInfo == nullptr &&
+                   !isCallParameterType(Op->getType())) {
+          // TODO(kschimpf): Move this check to the function declaration, so
+          // that it only needs to be checked once.
+          std::string Buffer;
+          raw_string_ostream StrBuf(Buffer);
+          StrBuf << "Call argument " << *Op
+                 << " matches declaration but has invalid type: "
+                 << Op->getType();
+          Error(StrBuf.str());
+        }
+      } else if (!isCallParameterType(Op->getType())) {
+        std::string Buffer;
+        raw_string_ostream StrBuf(Buffer);
+        StrBuf << "Call argument " << *Op
+               << " has invalid type: " << Op->getType();
+        Error(StrBuf.str());
+      }
+      Inst->addArg(Op);
     }
 
     // If intrinsic call, validate call signature.
     if (IntrinsicInfo) {
       Ice::SizeT ArgIndex = 0;
-      switch (IntrinsicInfo->validateCall(Inst, ArgIndex)) {
+      switch (IntrinsicInfo->validateCall(Inst.get(), ArgIndex)) {
       case Ice::Intrinsics::IsValidCall:
         break;
       case Ice::Intrinsics::BadReturnType: {
@@ -2750,7 +2807,7 @@
       }
     }
 
-    CurrentNode->appendInst(Inst);
+    CurrentNode->appendInst(Inst.release());
     return;
   }
   case naclbitc::FUNC_CODE_INST_FORWARDTYPEREF: {
diff --git a/tests_lit/parse_errs/call-fcn-bad-param-type.ll b/tests_lit/parse_errs/call-fcn-bad-param-type.ll
new file mode 100644
index 0000000..2314732
--- /dev/null
+++ b/tests_lit/parse_errs/call-fcn-bad-param-type.ll
@@ -0,0 +1,15 @@
+; Test that even if a call parameter matches its declaration, it must still
+; be a legal call parameter type (unless declaration is intrinsic).
+
+; REQUIRES: no_minimal_build
+
+; RUN: %p2i --expect-fail -i %s --insts | FileCheck %s
+
+declare void @f(i8);
+
+define void @Test() {
+entry:
+  call void @f(i8 1)
+; CHECK: Call argument 1 matches declaration but has invalid type: i8
+  ret void
+}
diff --git a/tests_lit/parse_errs/call-fcn-bad-return-type.ll b/tests_lit/parse_errs/call-fcn-bad-return-type.ll
new file mode 100644
index 0000000..b1c8cd8
--- /dev/null
+++ b/tests_lit/parse_errs/call-fcn-bad-return-type.ll
@@ -0,0 +1,16 @@
+; Test that even if a call return type matches its declaration, it must still be
+; a legal call return type (unless declaration is intrinsic).
+
+; REQUIRES: no_minimal_build
+
+; RUN: %p2i --expect-fail -i %s --insts | FileCheck %s
+
+declare i1 @f();
+
+define void @Test() {
+entry:
+  %v = call i1 @f()
+; CHECK: Return type of called function is invalid: i1
+  ret void
+}
+
diff --git a/tests_lit/parse_errs/call-indirect-i8.ll b/tests_lit/parse_errs/call-indirect-i8.ll
new file mode 100644
index 0000000..0a3b2bc
--- /dev/null
+++ b/tests_lit/parse_errs/call-indirect-i8.ll
@@ -0,0 +1,13 @@
+; Tests that we don't allow illegal sized parameters on indirect calls.
+
+; REQUIRES: no_minimal_build
+
+; RUN: %p2i --expect-fail -i %s --insts | FileCheck %s
+
+define void @CallIndirectI32(i32 %f_addr) {
+entry:
+  %f = inttoptr i32 %f_addr to i32(i8)*
+  %r = call i32 %f(i8 1)
+; CHECK: Call argument 1 has invalid type: i8
+  ret void
+}
diff --git a/tests_lit/reader_tests/call-indirect.ll b/tests_lit/reader_tests/call-indirect.ll
index 3da87dd..d3d7e1d 100644
--- a/tests_lit/reader_tests/call-indirect.ll
+++ b/tests_lit/reader_tests/call-indirect.ll
@@ -1,10 +1,7 @@
 ; Test parsing indirect calls in Subzero.
 
 ; RUN: %p2i -i %s --insts | FileCheck %s
-; RUN: %if --need=allow_disable_ir_gen --command \
-; RUN:   %p2i -i %s --args -notranslate -timing -no-ir-gen \
-; RUN: | %if --need=allow_disable_ir_gen --command \
-; RUN:   FileCheck --check-prefix=NOIR %s
+; RUN: %p2i -i %s --args -notranslate -timing | FileCheck --check-prefix=NOIR %s
 
 define internal void @CallIndirectVoid(i32 %f_addr) {
 entry:
@@ -21,14 +18,14 @@
 
 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)
+  %f = inttoptr i32 %f_addr to i32(i64, i32)*
+  %r = call i32 %f(i64 1, i32 %f_addr)
   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:   %r = call i32 %f_addr(i64 1, i32 %f_addr)
 ; CHECK-NEXT:   ret i32 %r
 ; CHECK-NEXT: }