OSDN Git Service

Subzero, MIPS32: Introduction of genTargetHelperCallFor
authorSrdjan Obucina <srdjan.obucina@imgtec.com>
Fri, 9 Sep 2016 16:39:52 +0000 (09:39 -0700)
committerJim Stichnoth <stichnot@chromium.org>
Fri, 9 Sep 2016 16:39:52 +0000 (09:39 -0700)
genTargetHelperCallFor provides framework for calling Subzero
runtime functions. This patch implements calls and tests for
some of the available functions, but not all. Current
lowerCall implementation does not allow more (Handling of FP
return values is missing).

R=stichnot@chromium.org

Review URL: https://codereview.chromium.org/2317653004 .

Patch from Srdjan Obucina <srdjan.obucina@imgtec.com>.

src/IceTargetLoweringMIPS32.cpp
src/IceTargetLoweringMIPS32.h
tests_lit/llvm2ice_tests/64bit.pnacl.ll
tests_lit/llvm2ice_tests/arith.ll
tests_lit/llvm2ice_tests/fp.arith.ll
tests_lit/llvm2ice_tests/nacl-mem-intrinsics.ll
tests_lit/llvm2ice_tests/nacl-other-intrinsics.ll

index 7bdf723..dd6133a 100644 (file)
@@ -215,6 +215,381 @@ uint32_t TargetMIPS32::getStackAlignment() const {
   return MIPS32_STACK_ALIGNMENT_BYTES;
 }
 
+void TargetMIPS32::genTargetHelperCallFor(Inst *Instr) {
+  constexpr bool NoTailCall = false;
+  constexpr bool IsTargetHelperCall = true;
+
+  switch (Instr->getKind()) {
+  default:
+    return;
+  case Inst::Arithmetic: {
+    Variable *Dest = Instr->getDest();
+    const Type DestTy = Dest->getType();
+    const InstArithmetic::OpKind Op =
+        llvm::cast<InstArithmetic>(Instr)->getOp();
+    if (isVectorType(DestTy)) {
+      switch (Op) {
+      default:
+        break;
+      case InstArithmetic::Fdiv:
+      case InstArithmetic::Frem:
+      case InstArithmetic::Sdiv:
+      case InstArithmetic::Srem:
+      case InstArithmetic::Udiv:
+      case InstArithmetic::Urem:
+        scalarizeArithmetic(Op, Dest, Instr->getSrc(0), Instr->getSrc(1));
+        Instr->setDeleted();
+        return;
+      }
+    }
+    switch (DestTy) {
+    default:
+      return;
+    case IceType_i64: {
+      RuntimeHelper HelperID = RuntimeHelper::H_Num;
+      switch (Op) {
+      default:
+        return;
+      case InstArithmetic::Udiv:
+        HelperID = RuntimeHelper::H_udiv_i64;
+        break;
+      case InstArithmetic::Sdiv:
+        HelperID = RuntimeHelper::H_sdiv_i64;
+        break;
+      case InstArithmetic::Urem:
+        HelperID = RuntimeHelper::H_urem_i64;
+        break;
+      case InstArithmetic::Srem:
+        HelperID = RuntimeHelper::H_srem_i64;
+        break;
+      }
+
+      if (HelperID == RuntimeHelper::H_Num) {
+        return;
+      }
+
+      Operand *TargetHelper = Ctx->getRuntimeHelperFunc(HelperID);
+      constexpr SizeT MaxArgs = 2;
+      auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      Call->addArg(Instr->getSrc(0));
+      Call->addArg(Instr->getSrc(1));
+      Instr->setDeleted();
+      return;
+    }
+    case IceType_i32:
+    case IceType_i16:
+    case IceType_i8: {
+      InstCast::OpKind CastKind;
+      RuntimeHelper HelperID = RuntimeHelper::H_Num;
+      switch (Op) {
+      default:
+        return;
+      case InstArithmetic::Udiv:
+        HelperID = RuntimeHelper::H_udiv_i32;
+        CastKind = InstCast::Zext;
+        break;
+      case InstArithmetic::Sdiv:
+        HelperID = RuntimeHelper::H_sdiv_i32;
+        CastKind = InstCast::Sext;
+        break;
+      case InstArithmetic::Urem:
+        HelperID = RuntimeHelper::H_urem_i32;
+        CastKind = InstCast::Zext;
+        break;
+      case InstArithmetic::Srem:
+        HelperID = RuntimeHelper::H_srem_i32;
+        CastKind = InstCast::Sext;
+        break;
+      }
+
+      if (HelperID == RuntimeHelper::H_Num) {
+        return;
+      }
+
+      Operand *Src0 = Instr->getSrc(0);
+      Operand *Src1 = Instr->getSrc(1);
+      if (DestTy != IceType_i32) {
+        // Src0 and Src1 have to be zero-, or signed-extended to i32. For Src0,
+        // we just insert a InstCast right before the call to the helper.
+        Variable *Src0_32 = Func->makeVariable(IceType_i32);
+        Context.insert<InstCast>(CastKind, Src0_32, Src0);
+        Src0 = Src0_32;
+
+        if (auto *C = llvm::dyn_cast<ConstantInteger32>(Src1)) {
+          const int32_t ShAmt = (DestTy == IceType_i16) ? 16 : 24;
+          int32_t NewC = C->getValue();
+          if (CastKind == InstCast::Zext) {
+            NewC &= ~(0x80000000l >> ShAmt);
+          } else {
+            NewC = (NewC << ShAmt) >> ShAmt;
+          }
+          Src1 = Ctx->getConstantInt32(NewC);
+        } else {
+          Variable *Src1_32 = Func->makeVariable(IceType_i32);
+          Context.insert<InstCast>(CastKind, Src1_32, Src1);
+          Src1 = Src1_32;
+        }
+      }
+      Operand *TargetHelper = Ctx->getRuntimeHelperFunc(HelperID);
+      constexpr SizeT MaxArgs = 2;
+      auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      assert(Src0->getType() == IceType_i32);
+      Call->addArg(Src0);
+      assert(Src1->getType() == IceType_i32);
+      Call->addArg(Src1);
+      Instr->setDeleted();
+      return;
+    }
+    case IceType_f32:
+    case IceType_f64: {
+      if (Op != InstArithmetic::Frem) {
+        return;
+      }
+      constexpr SizeT MaxArgs = 2;
+      Operand *TargetHelper = Ctx->getRuntimeHelperFunc(
+          DestTy == IceType_f32 ? RuntimeHelper::H_frem_f32
+                                : RuntimeHelper::H_frem_f64);
+      auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      Call->addArg(Instr->getSrc(0));
+      Call->addArg(Instr->getSrc(1));
+      Instr->setDeleted();
+      return;
+    }
+    }
+    llvm::report_fatal_error("Control flow should never have reached here.");
+  }
+  case Inst::Cast: {
+    Variable *Dest = Instr->getDest();
+    Operand *Src0 = Instr->getSrc(0);
+    const Type DestTy = Dest->getType();
+    const Type SrcTy = Src0->getType();
+    auto *CastInstr = llvm::cast<InstCast>(Instr);
+    const InstCast::OpKind CastKind = CastInstr->getCastKind();
+
+    switch (CastKind) {
+    default:
+      return;
+    case InstCast::Fptosi:
+    case InstCast::Fptoui: {
+      if (DestTy != IceType_i64) {
+        return;
+      }
+      const bool DestIsSigned = CastKind == InstCast::Fptosi;
+      const bool Src0IsF32 = isFloat32Asserting32Or64(SrcTy);
+      Operand *TargetHelper = Ctx->getRuntimeHelperFunc(
+          Src0IsF32 ? (DestIsSigned ? RuntimeHelper::H_fptosi_f32_i64
+                                    : RuntimeHelper::H_fptoui_f32_i64)
+                    : (DestIsSigned ? RuntimeHelper::H_fptosi_f64_i64
+                                    : RuntimeHelper::H_fptoui_f64_i64));
+      static constexpr SizeT MaxArgs = 1;
+      auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      Call->addArg(Src0);
+      Instr->setDeleted();
+      return;
+    }
+    case InstCast::Sitofp:
+    case InstCast::Uitofp: {
+      if (SrcTy != IceType_i64) {
+        return;
+      }
+      const bool SourceIsSigned = CastKind == InstCast::Sitofp;
+      const bool DestIsF32 = isFloat32Asserting32Or64(Dest->getType());
+      Operand *TargetHelper = Ctx->getRuntimeHelperFunc(
+          DestIsF32 ? (SourceIsSigned ? RuntimeHelper::H_sitofp_i64_f32
+                                      : RuntimeHelper::H_uitofp_i64_f32)
+                    : (SourceIsSigned ? RuntimeHelper::H_sitofp_i64_f64
+                                      : RuntimeHelper::H_uitofp_i64_f64));
+      static constexpr SizeT MaxArgs = 1;
+      auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      Call->addArg(Src0);
+      Instr->setDeleted();
+      return;
+    }
+    case InstCast::Bitcast: {
+      if (DestTy == SrcTy) {
+        return;
+      }
+      Variable *CallDest = Dest;
+      RuntimeHelper HelperID = RuntimeHelper::H_Num;
+      switch (DestTy) {
+      default:
+        return;
+      case IceType_i8:
+        assert(SrcTy == IceType_v8i1);
+        HelperID = RuntimeHelper::H_bitcast_8xi1_i8;
+        CallDest = Func->makeVariable(IceType_i32);
+        break;
+      case IceType_i16:
+        assert(SrcTy == IceType_v16i1);
+        HelperID = RuntimeHelper::H_bitcast_16xi1_i16;
+        CallDest = Func->makeVariable(IceType_i32);
+        break;
+      case IceType_v8i1: {
+        assert(SrcTy == IceType_i8);
+        HelperID = RuntimeHelper::H_bitcast_i8_8xi1;
+        Variable *Src0AsI32 = Func->makeVariable(stackSlotType());
+        // Arguments to functions are required to be at least 32 bits wide.
+        Context.insert<InstCast>(InstCast::Zext, Src0AsI32, Src0);
+        Src0 = Src0AsI32;
+      } break;
+      case IceType_v16i1: {
+        assert(SrcTy == IceType_i16);
+        HelperID = RuntimeHelper::H_bitcast_i16_16xi1;
+        Variable *Src0AsI32 = Func->makeVariable(stackSlotType());
+        // Arguments to functions are required to be at least 32 bits wide.
+        Context.insert<InstCast>(InstCast::Zext, Src0AsI32, Src0);
+        Src0 = Src0AsI32;
+      } break;
+      }
+      constexpr SizeT MaxSrcs = 1;
+      InstCall *Call = makeHelperCall(HelperID, CallDest, MaxSrcs);
+      Call->addArg(Src0);
+      Context.insert(Call);
+      // The PNaCl ABI disallows i8/i16 return types, so truncate the helper
+      // call result to the appropriate type as necessary.
+      if (CallDest->getType() != Dest->getType())
+        Context.insert<InstCast>(InstCast::Trunc, Dest, CallDest);
+      Instr->setDeleted();
+      return;
+    }
+    case InstCast::Trunc: {
+      if (DestTy == SrcTy) {
+        return;
+      }
+      if (!isVectorType(SrcTy)) {
+        return;
+      }
+      assert(typeNumElements(DestTy) == typeNumElements(SrcTy));
+      assert(typeElementType(DestTy) == IceType_i1);
+      assert(isVectorIntegerType(SrcTy));
+      return;
+    }
+    case InstCast::Sext:
+    case InstCast::Zext: {
+      if (DestTy == SrcTy) {
+        return;
+      }
+      if (!isVectorType(DestTy)) {
+        return;
+      }
+      assert(typeNumElements(DestTy) == typeNumElements(SrcTy));
+      assert(typeElementType(SrcTy) == IceType_i1);
+      assert(isVectorIntegerType(DestTy));
+      return;
+    }
+    }
+    llvm::report_fatal_error("Control flow should never have reached here.");
+  }
+  case Inst::IntrinsicCall: {
+    Variable *Dest = Instr->getDest();
+    auto *IntrinsicCall = llvm::cast<InstIntrinsicCall>(Instr);
+    Intrinsics::IntrinsicID ID = IntrinsicCall->getIntrinsicInfo().ID;
+    switch (ID) {
+    default:
+      return;
+    case Intrinsics::Ctpop: {
+      Operand *Src0 = IntrinsicCall->getArg(0);
+      Operand *TargetHelper =
+          Ctx->getRuntimeHelperFunc(isInt32Asserting32Or64(Src0->getType())
+                                        ? RuntimeHelper::H_call_ctpop_i32
+                                        : RuntimeHelper::H_call_ctpop_i64);
+      static constexpr SizeT MaxArgs = 1;
+      auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      Call->addArg(Src0);
+      Instr->setDeleted();
+      return;
+    }
+    case Intrinsics::Longjmp: {
+      static constexpr SizeT MaxArgs = 2;
+      static constexpr Variable *NoDest = nullptr;
+      Operand *TargetHelper =
+          Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_longjmp);
+      auto *Call = Context.insert<InstCall>(MaxArgs, NoDest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      Call->addArg(IntrinsicCall->getArg(0));
+      Call->addArg(IntrinsicCall->getArg(1));
+      Instr->setDeleted();
+      return;
+    }
+    case Intrinsics::Memcpy: {
+      static constexpr SizeT MaxArgs = 3;
+      static constexpr Variable *NoDest = nullptr;
+      Operand *TargetHelper =
+          Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_memcpy);
+      auto *Call = Context.insert<InstCall>(MaxArgs, NoDest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      Call->addArg(IntrinsicCall->getArg(0));
+      Call->addArg(IntrinsicCall->getArg(1));
+      Call->addArg(IntrinsicCall->getArg(2));
+      Instr->setDeleted();
+      return;
+    }
+    case Intrinsics::Memmove: {
+      static constexpr SizeT MaxArgs = 3;
+      static constexpr Variable *NoDest = nullptr;
+      Operand *TargetHelper =
+          Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_memmove);
+      auto *Call = Context.insert<InstCall>(MaxArgs, NoDest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      Call->addArg(IntrinsicCall->getArg(0));
+      Call->addArg(IntrinsicCall->getArg(1));
+      Call->addArg(IntrinsicCall->getArg(2));
+      Instr->setDeleted();
+      return;
+    }
+    case Intrinsics::Memset: {
+      Operand *ValOp = IntrinsicCall->getArg(1);
+      assert(ValOp->getType() == IceType_i8);
+      Variable *ValExt = Func->makeVariable(stackSlotType());
+      Context.insert<InstCast>(InstCast::Zext, ValExt, ValOp);
+
+      static constexpr SizeT MaxArgs = 3;
+      static constexpr Variable *NoDest = nullptr;
+      Operand *TargetHelper =
+          Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_memset);
+      auto *Call = Context.insert<InstCall>(MaxArgs, NoDest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      Call->addArg(IntrinsicCall->getArg(0));
+      Call->addArg(ValExt);
+      Call->addArg(IntrinsicCall->getArg(2));
+      Instr->setDeleted();
+      return;
+    }
+    case Intrinsics::NaClReadTP: {
+      if (SandboxingType == ST_NaCl) {
+        return;
+      }
+      static constexpr SizeT MaxArgs = 0;
+      assert(SandboxingType != ST_Nonsfi);
+      Operand *TargetHelper =
+          Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_read_tp);
+      Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, NoTailCall,
+                               IsTargetHelperCall);
+      Instr->setDeleted();
+      return;
+    }
+    case Intrinsics::Setjmp: {
+      static constexpr SizeT MaxArgs = 1;
+      Operand *TargetHelper =
+          Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_setjmp);
+      auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper,
+                                            NoTailCall, IsTargetHelperCall);
+      Call->addArg(IntrinsicCall->getArg(0));
+      Instr->setDeleted();
+      return;
+    }
+    }
+    llvm::report_fatal_error("Control flow should never have reached here.");
+  }
+  }
+}
+
 void TargetMIPS32::findMaxStackOutArgsSize() {
   // MinNeededOutArgsBytes should be updated if the Target ever creates a
   // high-level InstCall that requires more stack bytes.
@@ -1830,8 +2205,7 @@ void TargetMIPS32::lowerCall(const InstCall *Instr) {
       ReturnReg = makeReg(Dest->getType(), RegMIPS32::Reg_F0);
       break;
     case IceType_f64:
-      ReturnReg = makeReg(IceType_f32, RegMIPS32::Reg_F0);
-      ReturnRegHi = makeReg(IceType_f32, RegMIPS32::Reg_F1);
+      ReturnReg = makeReg(IceType_f64, RegMIPS32::Reg_F0);
       break;
     case IceType_v4i1:
     case IceType_v8i1:
@@ -1907,8 +2281,9 @@ void TargetMIPS32::lowerCall(const InstCall *Instr) {
     } else {
       assert(Dest->getType() == IceType_i32 || Dest->getType() == IceType_i16 ||
              Dest->getType() == IceType_i8 || Dest->getType() == IceType_i1 ||
+             isScalarFloatingType(Dest->getType()) ||
              isVectorType(Dest->getType()));
-      if (isFloatingType(Dest->getType()) || isVectorType(Dest->getType())) {
+      if (isVectorType(Dest->getType())) {
         UnimplementedLoweringError(this, Instr);
         return;
       } else {
@@ -2302,7 +2677,7 @@ void TargetMIPS32::lowerIntrinsicCall(const InstIntrinsicCall *Instr) {
 
 void TargetMIPS32::lowerLoad(const InstLoad *Instr) {
   // A Load instruction can be treated the same as an Assign instruction, after
-  // the source operand is transformed into an OperandARM32Mem operand.
+  // the source operand is transformed into an OperandMIPS32Mem operand.
   Type Ty = Instr->getDest()->getType();
   Operand *Src0 = formMemoryOperand(Instr->getSourceAddress(), Ty);
   Variable *DestLoad = Instr->getDest();
index b555b9b..8ccc982 100644 (file)
@@ -565,7 +565,7 @@ protected:
     (void)Instr;
     return 0;
   }
-  void genTargetHelperCallFor(Inst *Instr) override { (void)Instr; }
+  void genTargetHelperCallFor(Inst *Instr) override;
   void doAddressOptLoad() override;
   void doAddressOptStore() override;
   void randomlyInsertNop(float Probability,
index 5b678b5..74e4323 100644 (file)
@@ -408,6 +408,9 @@ entry:
 ; ARM32: bne
 ; ARM32: bl {{.*}} __divdi3
 
+; MIPS32-LABEL: div64BitSigned
+; MIPS32: jal {{.*}} __divdi3
+
 define internal i64 @div64BitSignedConst(i64 %a) {
 entry:
   %div = sdiv i64 %a, 12345678901234
@@ -431,6 +434,9 @@ entry:
 ; ARM32: movw {{.*}} ; 0xb3a
 ; ARM32: bl {{.*}} __divdi3
 
+; MIPS32-LABEL: div64BitSignedConst
+; MIPS32: jal {{.*}} __divdi3
+
 define internal i64 @div64BitUnsigned(i64 %a, i64 %b) {
 entry:
   %div = udiv i64 %a, %b
@@ -447,6 +453,9 @@ entry:
 ; ARM32: bne
 ; ARM32: bl {{.*}} __udivdi3
 
+; MIPS32-LABEL: div64BitUnsigned
+; MIPS32: jal {{.*}} __udivdi3
+
 define internal i64 @rem64BitSigned(i64 %a, i64 %b) {
 entry:
   %rem = srem i64 %a, %b
@@ -463,6 +472,9 @@ entry:
 ; ARM32: bne
 ; ARM32: bl {{.*}} __moddi3
 
+; MIPS32-LABEL: rem64BitSigned
+; MIPS32: jal {{.*}} __moddi3
+
 define internal i64 @rem64BitUnsigned(i64 %a, i64 %b) {
 entry:
   %rem = urem i64 %a, %b
@@ -479,6 +491,9 @@ entry:
 ; ARM32: bne
 ; ARM32: bl {{.*}} __umoddi3
 
+; MIPS32-LABEL: rem64BitUnsigned
+; MIPS32: jal {{.*}} __umoddi3
+
 define internal i64 @shl64BitSigned(i64 %a, i64 %b) {
 entry:
   %shl = shl i64 %a, %b
index 73ed882..9d6a110 100644 (file)
@@ -171,8 +171,7 @@ entry:
 ; ARM32HWDIV: sdiv
 
 ; MIPS32-LABEL: Sdiv
-; MIPS32: div
-; MIPS32: mflo
+; MIPS32: jal {{.*}} __divsi3
 
 define internal i32 @SdivConst(i32 %a) {
 entry:
@@ -191,8 +190,7 @@ entry:
 ; ARM32HWDIV: sdiv
 
 ; MIPS32-LABEL: SdivConst
-; MIPS32: div
-; MIPS32: mflo
+; MIPS32: jal {{.*}} __divsi3
 
 define internal i32 @Srem(i32 %a, i32 %b) {
 entry:
@@ -214,8 +212,7 @@ entry:
 ; ARM32HWDIV: mls
 
 ; MIPS32-LABEL: Srem
-; MIPS32: div
-; MIPS32: mfhi
+; MIPS32: jal {{.*}} __modsi3
 
 define internal i32 @Udiv(i32 %a, i32 %b) {
 entry:
@@ -235,8 +232,7 @@ entry:
 ; ARM32HWDIV: udiv
 
 ; MIPS32-LABEL: Udiv
-; MIPS32: divu
-; MIPS32: mflo
+; MIPS32: jal {{.*}} __udivsi3
 
 define internal i32 @Urem(i32 %a, i32 %b) {
 entry:
@@ -257,8 +253,7 @@ entry:
 ; ARM32HWDIV: mls
 
 ; MIPS32-LABEL: Urem
-; MIPS32: divu
-; MIPS32: mfhi
+; MIPS32: jal {{.*}} __umodsi3
 
 ; The following tests check that shift instructions don't try to use a
 ; ConstantRelocatable as an immediate operand.
index 22d41fc..a115441 100644 (file)
@@ -145,6 +145,8 @@ entry:
 ; CHECK: call {{.*}} R_{{.*}} fmodf
 ; ARM32-LABEL: remFloat
 ; ARM32: bl {{.*}} fmodf
+; MIPS32-LABEL: remFloat
+; MIPS32: jal {{.*}} fmodf
 
 define internal double @remDouble(double %a, double %b) {
 entry:
@@ -155,3 +157,5 @@ entry:
 ; CHECK: call {{.*}} R_{{.*}} fmod
 ; ARM32-LABEL: remDouble
 ; ARM32: bl {{.*}} fmod
+; MIPS32-LABEL: remDouble
+; MIPS32: jal {{.*}} fmod
index 521687b..6d8fc4f 100644 (file)
 ; RUN:   | %if --need=target_ARM32 --need=allow_dump \
 ; RUN:   --command FileCheck --check-prefix ARM32 %s
 
+; RUN: %if --need=target_MIPS32 --need=allow_dump \
+; RUN:   --command %p2i --filetype=asm --assemble --disassemble --target mips32\
+; RUN:   -i %s --args -Om1 --skip-unimplemented \
+; RUN:   | %if --need=target_MIPS32 --need=allow_dump \
+; RUN:   --command FileCheck --check-prefix MIPS32 %s
+
 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)
@@ -34,6 +40,8 @@ entry:
 ; OM1: call  {{.*}} memcpy
 ; ARM32-LABEL: test_memcpy
 ; ARM32: bl {{.*}} memcpy
+; MIPS32-LABEL: test_memcpy
+; MIPS32: jal {{.*}} memcpy
 
 define internal void @test_memcpy_long_const_len(i32 %iptr_dst, i32 %iptr_src) {
 entry:
@@ -49,6 +57,8 @@ entry:
 ; OM1: call {{.*}} memcpy
 ; ARM32-LABEL: test_memcpy_long_const_len
 ; ARM32: bl {{.*}} memcpy
+; MIPS32-LABEL: test_memcpy_long_const_len
+; MIPS32: jal {{.*}} memcpy
 
 define internal void @test_memcpy_very_small_const_len(i32 %iptr_dst,
                                                        i32 %iptr_src) {
@@ -67,6 +77,8 @@ entry:
 ; OM1: call {{.*}} memcpy
 ; ARM32-LABEL: test_memcpy_very_small_const_len
 ; ARM32: bl {{.*}} memcpy
+; MIPS32-LABEL: test_memcpy_very_small_const_len
+; MIPS32: jal {{.*}} memcpy
 
 define internal void @test_memcpy_const_len_3(i32 %iptr_dst, i32 %iptr_src) {
 entry:
@@ -86,6 +98,8 @@ entry:
 ; OM1: call {{.*}} memcpy
 ; ARM32-LABEL: test_memcpy_const_len_3
 ; ARM32: bl {{.*}} memcpy
+; MIPS32-LABEL: test_memcpy_const_len_3
+; MIPS32: jal {{.*}} memcpy
 
 define internal void @test_memcpy_mid_const_len(i32 %iptr_dst, i32 %iptr_src) {
 entry:
@@ -105,6 +119,8 @@ entry:
 ; OM1: call {{.*}} memcpy
 ; ARM32-LABEL: test_memcpy_mid_const_len
 ; ARM32: bl {{.*}} memcpy
+; MIPS32-LABEL: test_memcpy_mid_const_len
+; MIPS32: jal {{.*}} memcpy
 
 define internal void @test_memcpy_mid_const_len_overlap(i32 %iptr_dst,
                                                         i32 %iptr_src) {
@@ -125,6 +141,8 @@ entry:
 ; OM1: call {{.*}} memcpy
 ; ARM32-LABEL: test_memcpy_mid_const_len_overlap
 ; ARM32: bl {{.*}} memcpy
+; MIPS32-LABEL: test_memcpy_mid_const_len_overlap
+; MIPS32: jal {{.*}} memcpy
 
 define internal void @test_memcpy_big_const_len_overlap(i32 %iptr_dst,
                                                         i32 %iptr_src) {
@@ -145,6 +163,8 @@ entry:
 ; OM1: call {{.*}} memcpy
 ; ARM32-LABEL: test_memcpy_big_const_len_overlap
 ; ARM32: bl {{.*}} memcpy
+; MIPS32-LABEL: test_memcpy_big_const_len_overlap
+; MIPS32: jal {{.*}} memcpy
 
 define internal void @test_memcpy_large_const_len(i32 %iptr_dst,
                                                   i32 %iptr_src) {
@@ -167,6 +187,8 @@ entry:
 ; OM1: call {{.*}} memcpy
 ; ARM32-LABEL: test_memcpy_large_const_len
 ; ARM32: bl {{.*}} memcpy
+; MIPS32-LABEL: test_memcpy_large_const_len
+; MIPS32: jal {{.*}} memcpy
 
 define internal void @test_memmove(i32 %iptr_dst, i32 %iptr_src, i32 %len) {
 entry:
@@ -182,6 +204,8 @@ entry:
 ; OM1: call {{.*}} memmove
 ; ARM32-LABEL: test_memmove
 ; ARM32: bl {{.*}} memmove
+; MIPS32-LABEL: test_memmove
+; MIPS32: jal {{.*}} memmove
 
 define internal void @test_memmove_long_const_len(i32 %iptr_dst,
                                                   i32 %iptr_src) {
@@ -198,6 +222,8 @@ entry:
 ; OM1: call {{.*}} memmove
 ; ARM32-LABEL: test_memmove_long_const_len
 ; ARM32: bl {{.*}} memmove
+; MIPS32-LABEL: test_memmove_long_const_len
+; MIPS32: jal {{.*}} memmove
 
 define internal void @test_memmove_very_small_const_len(i32 %iptr_dst,
                                                         i32 %iptr_src) {
@@ -216,6 +242,8 @@ entry:
 ; OM1: call {{.*}} memmove
 ; ARM32-LABEL: test_memmove_very_small_const_len
 ; ARM32: bl {{.*}} memmove
+; MIPS32-LABEL: test_memmove_very_small_const_len
+; MIPS32: jal {{.*}} memmove
 
 define internal void @test_memmove_const_len_3(i32 %iptr_dst, i32 %iptr_src) {
 entry:
@@ -235,6 +263,8 @@ entry:
 ; OM1: call {{.*}} memmove
 ; ARM32-LABEL: test_memmove_const_len_3
 ; ARM32: bl {{.*}} memmove
+; MIPS32-LABEL: test_memmove_const_len_3
+; MIPS32: jal {{.*}} memmove
 
 define internal void @test_memmove_mid_const_len(i32 %iptr_dst, i32 %iptr_src) {
 entry:
@@ -254,6 +284,8 @@ entry:
 ; OM1: call {{.*}} memmove
 ; ARM32-LABEL: test_memmove_mid_const_len
 ; ARM32: bl {{.*}} memmove
+; MIPS32-LABEL: test_memmove_mid_const_len
+; MIPS32: jal {{.*}} memmove
 
 define internal void @test_memmove_mid_const_len_overlap(i32 %iptr_dst,
                                                          i32 %iptr_src) {
@@ -274,6 +306,8 @@ entry:
 ; OM1: call {{.*}} memmove
 ; ARM32-LABEL: test_memmove_mid_const_len_overlap
 ; ARM32: bl {{.*}} memmove
+; MIPS32-LABEL: test_memmove_mid_const_len_overlap
+; MIPS32: jal {{.*}} memmove
 
 define internal void @test_memmove_big_const_len_overlap(i32 %iptr_dst,
                                                          i32 %iptr_src) {
@@ -294,6 +328,8 @@ entry:
 ; OM1: call {{.*}} memmove
 ; ARM32-LABEL: test_memmove_big_const_len_overlap
 ; ARM32: bl {{.*}} memmove
+; MIPS32-LABEL: test_memmove_big_const_len_overlap
+; MIPS32: jal {{.*}} memmove
 
 define internal void @test_memmove_large_const_len(i32 %iptr_dst,
                                                    i32 %iptr_src) {
@@ -316,6 +352,8 @@ entry:
 ; OM1: call {{.*}} memmove
 ; ARM32-LABEL: test_memmove_large_const_len
 ; ARM32: bl {{.*}} memmove
+; MIPS32-LABEL: test_memmove_large_const_len
+; MIPS32: jal {{.*}} memmove
 
 define internal void @test_memset(i32 %iptr_dst, i32 %wide_val, i32 %len) {
 entry:
@@ -334,6 +372,8 @@ entry:
 ; ARM32-LABEL: test_memset
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset
+; MIPS32: jal {{.*}} memset
 
 define internal void @test_memset_const_len_align(i32 %iptr_dst,
                                                   i32 %wide_val) {
@@ -353,6 +393,8 @@ entry:
 ; ARM32-LABEL: test_memset_const_len_align
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_const_len_align
+; MIPS32: jal {{.*}} memset
 
 define internal void @test_memset_long_const_len_zero_val_align(
     i32 %iptr_dst) {
@@ -369,6 +411,8 @@ entry:
 ; ARM32-LABEL: test_memset_long_const_len_zero_val_align
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_long_const_len_zero_val_align
+; MIPS32: jal {{.*}} memset
 
 define internal void @test_memset_const_val(i32 %iptr_dst, i32 %len) {
 entry:
@@ -384,6 +428,8 @@ entry:
 ; ARM32-LABEL: test_memset_const_val
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_const_val
+; MIPS32: jal {{.*}} memset
 
 define internal void @test_memset_const_val_len_very_small(i32 %iptr_dst) {
 entry:
@@ -399,6 +445,8 @@ entry:
 ; ARM32-LABEL: test_memset_const_val_len_very_small
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_const_val_len_very_small
+; MIPS32: jal {{.*}} memset
 
 define internal void @test_memset_const_val_len_3(i32 %iptr_dst) {
 entry:
@@ -415,6 +463,8 @@ entry:
 ; ARM32-LABEL: test_memset_const_val_len_3
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_const_val_len_3
+; MIPS32: jal {{.*}} memset
 
 define internal void @test_memset_const_val_len_mid(i32 %iptr_dst) {
 entry:
@@ -432,6 +482,8 @@ entry:
 ; ARM32-LABEL: test_memset_const_val_len_mid
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_const_val_len_mid
+; MIPS32: jal {{.*}} memset
 
 ; Same as above, but with a negative value.
 define internal void @test_memset_const_neg_val_len_mid(i32 %iptr_dst) {
@@ -450,6 +502,8 @@ entry:
 ; ARM32-LABEL: test_memset_const_neg_val_len_mid
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_const_neg_val_len_mid
+; MIPS32: jal {{.*}} memset
 
 define internal void @test_memset_zero_const_len_small(i32 %iptr_dst) {
 entry:
@@ -467,6 +521,8 @@ entry:
 ; ARM32-LABEL: test_memset_zero_const_len_small
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_zero_const_len_small
+; MIPS32: jal {{.*}} memset
 
 define internal void @test_memset_zero_const_len_small_overlap(i32 %iptr_dst) {
 entry:
@@ -484,6 +540,8 @@ entry:
 ; ARM32-LABEL: test_memset_zero_const_len_small_overlap
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_zero_const_len_small_overlap
+; MIPS32: jal {{.*}} memset
 
 define internal void @test_memset_zero_const_len_big_overlap(i32 %iptr_dst) {
 entry:
@@ -501,6 +559,8 @@ entry:
 ; ARM32-LABEL: test_memset_zero_const_len_big_overlap
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_zero_const_len_big_overlap
+; MIPS32: jal {{.*}} memset
 
 define internal void @test_memset_zero_const_len_large(i32 %iptr_dst) {
 entry:
@@ -519,3 +579,5 @@ entry:
 ; ARM32-LABEL: test_memset_zero_const_len_large
 ; ARM32: uxtb
 ; ARM32: bl {{.*}} memset
+; MIPS32-LABEL: test_memset_zero_const_len_large
+; MIPS32: jal {{.*}} memset
index 371db88..5af627b 100644 (file)
 ; RUN:   | %if --need=target_ARM32 --need=allow_dump \
 ; RUN:   --command FileCheck --check-prefix ARM32 %s
 
+; RUN: %if --need=target_MIPS32 --need=allow_dump \
+; RUN:   --command %p2i --filetype=asm --assemble --disassemble --target mips32\
+; RUN:   -i %s --args -Om1 --skip-unimplemented \
+; RUN:   -allow-externally-defined-symbols \
+; RUN:   | %if --need=target_MIPS32 --need=allow_dump \
+; RUN:   --command FileCheck --check-prefix MIPS32 %s
+
 declare i8* @llvm.nacl.read.tp()
 declare void @llvm.nacl.longjmp(i8*, i32)
 declare i32 @llvm.nacl.setjmp(i8*)
@@ -67,6 +74,8 @@ entry:
 ; CHECKO2REM: mov e{{.*}},{{(DWORD PTR )?}}gs:0x0
 ; CHECKO2UNSANDBOXEDREM-LABEL: test_nacl_read_tp
 ; CHECKO2UNSANDBOXEDREM: call {{.*}} R_{{.*}} __nacl_read_tp
+; MIPS32-LABEL: test_nacl_read_tp
+; MIPS32: jal {{.*}} __nacl_read_tp
 
 define internal i32 @test_nacl_read_tp_more_addressing() {
 entry:
@@ -94,6 +103,8 @@ entry:
 ; CHECKO2UNSANDBOXEDREM-LABEL: test_nacl_read_tp_more_addressing
 ; CHECKO2UNSANDBOXEDREM: call {{.*}} R_{{.*}} __nacl_read_tp
 ; CHECKO2UNSANDBOXEDREM: call {{.*}} R_{{.*}} __nacl_read_tp
+; MIPS32-LABEL: test_nacl_read_tp_more_addressing
+; MIPS32: jal {{.*}} __nacl_read_tp
 
 define internal i32 @test_nacl_read_tp_dead(i32 %a) {
 entry:
@@ -107,6 +118,8 @@ entry:
 ; CHECKO2REM-NOT: mov e{{.*}}, DWORD PTR gs:0x0
 ; CHECKO2UNSANDBOXEDREM-LABEL: test_nacl_read_tp_dead
 ; CHECKO2UNSANDBOXEDREM-NOT: call {{.*}} R_{{.*}} __nacl_read_tp
+; MIPS32-LABEL: test_nacl_read_tp_dead
+; MIPS32: jal {{.*}} __nacl_read_tp
 
 define internal i32 @test_setjmplongjmp(i32 %iptr_env) {
 entry: