Index: include/llvm/ADT/Triple.h
===================================================================
--- include/llvm/ADT/Triple.h
+++ include/llvm/ADT/Triple.h
@@ -191,7 +191,14 @@
     Cygnus,
     AMDOpenCL,
     CoreCLR,
-    LastEnvironmentType = CoreCLR
+    AndroidABI32,
+    AndroidABI64,
+    ABI32,
+    ABIN32,
+    ABI64,
+    GNUABI32,
+    GNUABIN32,
+    LastEnvironmentType = GNUABIN32
   };
   enum ObjectFormatType {
     UnknownObjectFormat,
@@ -477,7 +484,8 @@
 
   bool isGNUEnvironment() const {
     EnvironmentType Env = getEnvironment();
-    return Env == Triple::GNU || Env == Triple::GNUABI64 ||
+    return Env == Triple::GNU || Env == Triple::GNUABI32 ||
+           Env == Triple::GNUABIN32 || Env == Triple::GNUABI64 ||
            Env == Triple::GNUEABI || Env == Triple::GNUEABIHF ||
            Env == Triple::GNUX32;
   }
@@ -674,6 +682,22 @@
   ///          architecture if no such variant can be found.
   llvm::Triple getLittleEndianArchVariant() const;
 
+  /// Form a triple with variant of the current architecture and the specified
+  /// ABI.
+  ///
+  /// Generally speaking the target specifies the ABI in the triple if the ABI
+  /// has a large effect on the output (e.g. ELFCLASS32/ELFCLASS64, REL/RELA,
+  /// different sets of relocs) and/or is not link-compatible with other ABI's.
+  /// If the ABI is fairly small in effect (e.g. calling convention) then and/or
+  /// is link-compatible with other ABI's then MCTargetOptions is preferable.
+  ///
+  /// \param ABI The ABI name to use. If it is an empty string then the triple's
+  /// default is used.
+  /// \returns A new triple with the requested ABI or an unknown architecture
+  ///          if no such variant can be found. Also, an ABI name that is
+  ///          consistent with the triple.
+  std::pair<llvm::Triple, StringRef> getABIVariant(StringRef ABI) const;
+
   /// Get the (LLVM) name of the minimum ARM CPU for the arch we are targeting.
   ///
   /// \param Arch the architecture name (e.g., "armv7s"). If it is an empty
Index: lib/Support/Triple.cpp
===================================================================
--- lib/Support/Triple.cpp
+++ lib/Support/Triple.cpp
@@ -200,7 +200,12 @@
 const char *Triple::getEnvironmentTypeName(EnvironmentType Kind) {
   switch (Kind) {
   case UnknownEnvironment: return "unknown";
+  case ABI32: return "abi32";
+  case ABIN32: return "abin32";
+  case ABI64: return "abi64";
   case GNU: return "gnu";
+  case GNUABI32: return "gnuabi32";
+  case GNUABIN32: return "gnuabin32";
   case GNUABI64: return "gnuabi64";
   case GNUEABIHF: return "gnueabihf";
   case GNUEABI: return "gnueabi";
@@ -209,6 +214,8 @@
   case EABI: return "eabi";
   case EABIHF: return "eabihf";
   case Android: return "android";
+  case AndroidABI32: return "androidabi32";
+  case AndroidABI64: return "androidabi64";
   case Musl: return "musl";
   case MuslEABI: return "musleabi";
   case MuslEABIHF: return "musleabihf";
@@ -467,14 +474,21 @@
 
 static Triple::EnvironmentType parseEnvironment(StringRef EnvironmentName) {
   return StringSwitch<Triple::EnvironmentType>(EnvironmentName)
+    .StartsWith("abi32", Triple::ABI32)
+    .StartsWith("abin32", Triple::ABIN32)
+    .StartsWith("abi64", Triple::ABI64)
     .StartsWith("eabihf", Triple::EABIHF)
     .StartsWith("eabi", Triple::EABI)
+    .StartsWith("gnuabi32", Triple::GNUABI32)
+    .StartsWith("gnuabin32", Triple::GNUABIN32)
     .StartsWith("gnuabi64", Triple::GNUABI64)
     .StartsWith("gnueabihf", Triple::GNUEABIHF)
     .StartsWith("gnueabi", Triple::GNUEABI)
     .StartsWith("gnux32", Triple::GNUX32)
     .StartsWith("code16", Triple::CODE16)
     .StartsWith("gnu", Triple::GNU)
+    .StartsWith("androidabi32", Triple::AndroidABI32)
+    .StartsWith("androidabi64", Triple::AndroidABI64)
     .StartsWith("android", Triple::Android)
     .StartsWith("musleabihf", Triple::MuslEABIHF)
     .StartsWith("musleabi", Triple::MuslEABI)
@@ -1426,6 +1440,98 @@
   }
 }
 
+std::pair<llvm::Triple, StringRef> Triple::getABIVariant(StringRef ABI) const {
+  Triple T(*this);
+  switch (getArch()) {
+  default:
+    if (ABI == "")
+      return std::make_pair(T, ABI);
+    T.setArch(Triple::UnknownArch);
+    return std::make_pair(T, ABI);
+
+  case Triple::arm:
+  case Triple::armeb:
+  case Triple::aarch64:
+  case Triple::aarch64_be:
+  case Triple::thumb:
+  case Triple::thumbeb:
+    if (ABI == "" || ABI == "aapcs16" || ABI == "darwinpcs" ||
+        ABI.startswith("apcs") || ABI.startswith("aapcs"))
+      return std::make_pair(T, ABI);
+    T.setArch(Triple::UnknownArch);
+    return std::make_pair(T, ABI);
+
+  case Triple::ppc64:
+  case Triple::ppc64le:
+    if (ABI == "" || ABI.startswith("elfv1") || ABI.startswith("elfv2"))
+      return std::make_pair(T, ABI);
+    T.setArch(Triple::UnknownArch);
+    return std::make_pair(T, ABI);
+
+  case Triple::mips:
+  case Triple::mipsel:
+  case Triple::mips64:
+  case Triple::mips64el: {
+    EnvironmentType NewEnv;
+
+    if (ABI == "")
+      ABI = (getArch() == Triple::mips || getArch() == Triple::mipsel) ? "o32" : "n64";
+
+    switch (T.getEnvironment()) {
+    default:
+      T.setArch(Triple::UnknownArch);
+      return std::make_pair(T, ABI);
+
+    case Triple::Android:
+    case Triple::AndroidABI64:
+      NewEnv = StringSwitch<EnvironmentType>(ABI)
+                   .Case("o32", Triple::AndroidABI32)
+                   .Case("n64", Triple::AndroidABI64)
+                   .Default(Triple::UnknownEnvironment);
+      break;
+    case Triple::UnknownEnvironment:
+    case Triple::ABI32:
+    case Triple::ABIN32:
+    case Triple::ABI64:
+      NewEnv = StringSwitch<EnvironmentType>(ABI)
+                   .Case("o32", Triple::ABI32)
+                   .Case("n32", Triple::ABIN32)
+                   .Case("n64", Triple::ABI64)
+                   .Default(Triple::UnknownEnvironment);
+      break;
+
+    case Triple::GNU:
+    case Triple::GNUABI32:
+    case Triple::GNUABIN32:
+    case Triple::GNUABI64:
+      NewEnv = StringSwitch<EnvironmentType>(ABI)
+                   .Case("o32", Triple::GNUABI32)
+                   .Case("n32", Triple::GNUABIN32)
+                   .Case("n64", Triple::GNUABI64)
+                   .Default(Triple::UnknownEnvironment);
+      break;
+    }
+
+    if (NewEnv == Triple::UnknownEnvironment) {
+      T.setArch(Triple::UnknownArch);
+      return std::make_pair(T, ABI);
+    }
+
+    // We have to keep the Arch and Environment in sync at the moment due to
+    // a false correlation between mips/mipsel and O32 and mips64/mips64el and
+    // N64 that is required by the MIPS backend.
+    if (ABI == "o32")
+      T = T.get32BitArchVariant();
+    else if (ABI == "n32" || ABI == "n64")
+      T = T.get64BitArchVariant();
+
+    if (NewEnv != T.getEnvironment())
+      T.setEnvironment(NewEnv);
+    return std::make_pair(T, "");
+  }
+  }
+}
+
 StringRef Triple::getARMCPUForArch(StringRef MArch) const {
   if (MArch.empty())
     MArch = getArchName();
Index: lib/Target/Mips/MCTargetDesc/MipsABIInfo.cpp
===================================================================
--- lib/Target/Mips/MCTargetDesc/MipsABIInfo.cpp
+++ lib/Target/Mips/MCTargetDesc/MipsABIInfo.cpp
@@ -57,6 +57,15 @@
     return MipsABIInfo::N64();
   else if (!Options.getABIName().empty())
     llvm_unreachable("Unknown ABI option for MIPS");
+  else if (TT.getEnvironment() == Triple::ABI32 ||
+           TT.getEnvironment() == Triple::GNUABI32)
+    return MipsABIInfo::O32();
+  else if (TT.getEnvironment() == Triple::ABIN32 ||
+           TT.getEnvironment() == Triple::GNUABIN32)
+    return MipsABIInfo::N32();
+  else if (TT.getEnvironment() == Triple::ABI64 ||
+           TT.getEnvironment() == Triple::GNUABI64)
+    return MipsABIInfo::N64();
 
   if (TT.getArch() == Triple::mips64 || TT.getArch() == Triple::mips64el)
     return MipsABIInfo::N64();
Index: test/CodeGen/Mips/ehframe-indirect.ll
===================================================================
--- test/CodeGen/Mips/ehframe-indirect.ll
+++ test/CodeGen/Mips/ehframe-indirect.ll
@@ -1,7 +1,6 @@
 ; RUN: llc -mtriple=mipsel-linux-gnu < %s -asm-verbose -relocation-model=pic | FileCheck -check-prefixes=ALL,O32 %s
 ; RUN: llc -mtriple=mipsel-linux-android < %s -asm-verbose -relocation-model=pic | FileCheck -check-prefixes=ALL,O32 %s
 ; RUN: llc -mtriple=mips64el-linux-gnu -mabi=n32 < %s -asm-verbose -relocation-model=pic | FileCheck -check-prefixes=ALL,N32 %s
-; RUN: llc -mtriple=mips64el-linux-android -mabi=n32 < %s -asm-verbose -relocation-model=pic | FileCheck -check-prefixes=ALL,N32 %s
 ; RUN: llc -mtriple=mips64el-linux-gnu < %s -asm-verbose -relocation-model=pic | FileCheck -check-prefixes=ALL,N64 %s
 ; RUN: llc -mtriple=mips64el-linux-android < %s -asm-verbose -relocation-model=pic | FileCheck -check-prefixes=ALL,N64 %s
 
Index: test/CodeGen/Mips/fp16-promote.ll
===================================================================
--- test/CodeGen/Mips/fp16-promote.ll
+++ test/CodeGen/Mips/fp16-promote.ll
@@ -1,4 +1,4 @@
-; RUN: llc -asm-verbose=false -mtriple=mipsel-linux-gnueabi -relocation-model=pic < %s | FileCheck %s -check-prefix=CHECK-LIBCALL
+; RUN: llc -asm-verbose=false -mtriple=mipsel-linux-gnu -relocation-model=pic < %s | FileCheck %s -check-prefix=CHECK-LIBCALL
 
 ; CHECK-LIBCALL-LABEL: test_fadd:
 ; CHECK-LIBCALL: %call16(__gnu_h2f_ieee)
Index: test/MC/Mips/cpsetup.s
===================================================================
--- test/MC/Mips/cpsetup.s
+++ test/MC/Mips/cpsetup.s
@@ -1,21 +1,21 @@
 # RUN: llvm-mc -triple mips-unknown-linux -mabi=o32 -filetype=obj -o - %s | \
 # RUN:   llvm-objdump -d -r - | FileCheck -check-prefixes=ALL,O32 %s
 
-# RUN: llvm-mc -triple mips-unknown-unknown -mabi=o32 %s | \
+# RUN: llvm-mc -triple mips-unknown-linux -mabi=o32 %s | \
 # RUN:   FileCheck -check-prefixes=ALL,ASM,ASM-O32 %s
 
 # RUN: llvm-mc -triple mips64-unknown-linux -mabi=n32 -filetype=obj -o - %s | \
 # RUN:   llvm-objdump -d -r - | \
 # RUN:   FileCheck -check-prefixes=ALL,NXX,N32 %s
 
-# RUN: llvm-mc -triple mips64-unknown-unknown -mabi=n32 %s | \
+# RUN: llvm-mc -triple mips64-unknown-linux -mabi=n32 %s | \
 # RUN:   FileCheck -check-prefixes=ALL,ASM,ASM-N32 %s
 
 # RUN: llvm-mc -triple mips64-unknown-linux %s -filetype=obj -o - | \
 # RUN:   llvm-objdump -d -r - | \
 # RUN:   FileCheck -check-prefixes=ALL,NXX,N64 %s
 
-# RUN: llvm-mc -triple mips64-unknown-unknown %s | \
+# RUN: llvm-mc -triple mips64-unknown-linux %s | \
 # RUN:   FileCheck -check-prefixes=ALL,ASM,ASM-N64 %s
 
         .text
Index: test/MC/Mips/elf_reginfo.s
===================================================================
--- test/MC/Mips/elf_reginfo.s
+++ test/MC/Mips/elf_reginfo.s
@@ -1,5 +1,5 @@
 # These *MUST* match the output of gas compiled with the same triple and
-# corresponding options (-mabi=64 -> -mattr=+n64 for example).
+# corresponding options.
 
 # RUN: llvm-mc -filetype=obj -triple=mips64el-linux -mabi=n64 %s -o - \
 # RUN: | llvm-readobj -s | FileCheck --check-prefix=CHECK_64 %s
Index: test/MC/Mips/expansion-jal-sym-pic.s
===================================================================
--- test/MC/Mips/expansion-jal-sym-pic.s
+++ test/MC/Mips/expansion-jal-sym-pic.s
@@ -161,8 +161,8 @@
 # O32: addiu  $25, $25, %lo($tmp0)    # encoding: [0x27,0x39,A,A]
 # O32:                                #   fixup A - offset: 0, value: %lo($tmp0), kind:   fixup_Mips_LO16
 
-# N32: lw  $25, %got_disp($tmp0)($gp) # encoding: [0x8f,0x99,A,A]
-# N32:                                #   fixup A - offset: 0, value: %got_disp($tmp0), kind:   fixup_Mips_GOT_DISP
+# N32: lw  $25, %got_disp(.Ltmp0)($gp) # encoding: [0x8f,0x99,A,A]
+# N32:                                 #   fixup A - offset: 0, value: %got_disp(.Ltmp0), kind:   fixup_Mips_GOT_DISP
 
 # N64: ld  $25, %got_disp(.Ltmp0)($gp) # encoding: [0xdf,0x99,A,A]
 # N64:                                 #   fixup A - offset: 0, value: %got_disp(.Ltmp0), kind:   fixup_Mips_GOT_DISP
Index: test/MC/Mips/mips64-register-names-o32.s
===================================================================
--- test/MC/Mips/mips64-register-names-o32.s
+++ test/MC/Mips/mips64-register-names-o32.s
@@ -2,40 +2,40 @@
 # RUN:     -mabi=o32 | FileCheck %s
 
 # Check that the register names are mapped to their correct numbers for o32
-# Second byte of daddiu with $zero at rt contains the number of the source
+# Second byte of addiu with $zero at rt contains the number of the source
 # register.
 
 .set noat
-daddiu	$zero, $zero, 0 # CHECK: encoding: [0x64,0x00,0x00,0x00]
-daddiu	$at, $zero, 0   # CHECK: encoding: [0x64,0x01,0x00,0x00]
-daddiu	$v0, $zero, 0   # CHECK: encoding: [0x64,0x02,0x00,0x00]
-daddiu	$v1, $zero, 0   # CHECK: encoding: [0x64,0x03,0x00,0x00]
-daddiu	$a0, $zero, 0   # CHECK: encoding: [0x64,0x04,0x00,0x00]
-daddiu	$a1, $zero, 0   # CHECK: encoding: [0x64,0x05,0x00,0x00]
-daddiu	$a2, $zero, 0   # CHECK: encoding: [0x64,0x06,0x00,0x00]
-daddiu	$a3, $zero, 0   # CHECK: encoding: [0x64,0x07,0x00,0x00]
-daddiu	$t0, $zero, 0   # CHECK: encoding: [0x64,0x08,0x00,0x00]
-daddiu	$t1, $zero, 0   # CHECK: encoding: [0x64,0x09,0x00,0x00]
-daddiu	$t2, $zero, 0   # CHECK: encoding: [0x64,0x0a,0x00,0x00]
-daddiu	$t3, $zero, 0   # CHECK: encoding: [0x64,0x0b,0x00,0x00]
-daddiu	$t4, $zero, 0   # CHECK: encoding: [0x64,0x0c,0x00,0x00]
-daddiu	$t5, $zero, 0   # CHECK: encoding: [0x64,0x0d,0x00,0x00]
-daddiu	$t6, $zero, 0   # CHECK: encoding: [0x64,0x0e,0x00,0x00]
-daddiu	$t7, $zero, 0   # CHECK: encoding: [0x64,0x0f,0x00,0x00]
-daddiu	$s0, $zero, 0   # CHECK: encoding: [0x64,0x10,0x00,0x00]
-daddiu	$s1, $zero, 0   # CHECK: encoding: [0x64,0x11,0x00,0x00]
-daddiu	$s2, $zero, 0   # CHECK: encoding: [0x64,0x12,0x00,0x00]
-daddiu	$s3, $zero, 0   # CHECK: encoding: [0x64,0x13,0x00,0x00]
-daddiu	$s4, $zero, 0   # CHECK: encoding: [0x64,0x14,0x00,0x00]
-daddiu	$s5, $zero, 0   # CHECK: encoding: [0x64,0x15,0x00,0x00]
-daddiu	$s6, $zero, 0   # CHECK: encoding: [0x64,0x16,0x00,0x00]
-daddiu	$s7, $zero, 0   # CHECK: encoding: [0x64,0x17,0x00,0x00]
-daddiu	$t8, $zero, 0   # CHECK: encoding: [0x64,0x18,0x00,0x00]
-daddiu	$t9, $zero, 0   # CHECK: encoding: [0x64,0x19,0x00,0x00]
-daddiu	$k0, $zero, 0   # CHECK: encoding: [0x64,0x1a,0x00,0x00]
-daddiu	$k1, $zero, 0   # CHECK: encoding: [0x64,0x1b,0x00,0x00]
-daddiu	$gp, $zero, 0   # CHECK: encoding: [0x64,0x1c,0x00,0x00]
-daddiu	$sp, $zero, 0   # CHECK: encoding: [0x64,0x1d,0x00,0x00]
-daddiu	$fp, $zero, 0   # CHECK: encoding: [0x64,0x1e,0x00,0x00]
-daddiu	$s8, $zero, 0   # CHECK: encoding: [0x64,0x1e,0x00,0x00]
-daddiu	$ra, $zero, 0   # CHECK: encoding: [0x64,0x1f,0x00,0x00]
+addiu	$zero, $zero, 0 # CHECK: encoding: [0x24,0x00,0x00,0x00]
+addiu	$at, $zero, 0   # CHECK: encoding: [0x24,0x01,0x00,0x00]
+addiu	$v0, $zero, 0   # CHECK: encoding: [0x24,0x02,0x00,0x00]
+addiu	$v1, $zero, 0   # CHECK: encoding: [0x24,0x03,0x00,0x00]
+addiu	$a0, $zero, 0   # CHECK: encoding: [0x24,0x04,0x00,0x00]
+addiu	$a1, $zero, 0   # CHECK: encoding: [0x24,0x05,0x00,0x00]
+addiu	$a2, $zero, 0   # CHECK: encoding: [0x24,0x06,0x00,0x00]
+addiu	$a3, $zero, 0   # CHECK: encoding: [0x24,0x07,0x00,0x00]
+addiu	$t0, $zero, 0   # CHECK: encoding: [0x24,0x08,0x00,0x00]
+addiu	$t1, $zero, 0   # CHECK: encoding: [0x24,0x09,0x00,0x00]
+addiu	$t2, $zero, 0   # CHECK: encoding: [0x24,0x0a,0x00,0x00]
+addiu	$t3, $zero, 0   # CHECK: encoding: [0x24,0x0b,0x00,0x00]
+addiu	$t4, $zero, 0   # CHECK: encoding: [0x24,0x0c,0x00,0x00]
+addiu	$t5, $zero, 0   # CHECK: encoding: [0x24,0x0d,0x00,0x00]
+addiu	$t6, $zero, 0   # CHECK: encoding: [0x24,0x0e,0x00,0x00]
+addiu	$t7, $zero, 0   # CHECK: encoding: [0x24,0x0f,0x00,0x00]
+addiu	$s0, $zero, 0   # CHECK: encoding: [0x24,0x10,0x00,0x00]
+addiu	$s1, $zero, 0   # CHECK: encoding: [0x24,0x11,0x00,0x00]
+addiu	$s2, $zero, 0   # CHECK: encoding: [0x24,0x12,0x00,0x00]
+addiu	$s3, $zero, 0   # CHECK: encoding: [0x24,0x13,0x00,0x00]
+addiu	$s4, $zero, 0   # CHECK: encoding: [0x24,0x14,0x00,0x00]
+addiu	$s5, $zero, 0   # CHECK: encoding: [0x24,0x15,0x00,0x00]
+addiu	$s6, $zero, 0   # CHECK: encoding: [0x24,0x16,0x00,0x00]
+addiu	$s7, $zero, 0   # CHECK: encoding: [0x24,0x17,0x00,0x00]
+addiu	$t8, $zero, 0   # CHECK: encoding: [0x24,0x18,0x00,0x00]
+addiu	$t9, $zero, 0   # CHECK: encoding: [0x24,0x19,0x00,0x00]
+addiu	$k0, $zero, 0   # CHECK: encoding: [0x24,0x1a,0x00,0x00]
+addiu	$k1, $zero, 0   # CHECK: encoding: [0x24,0x1b,0x00,0x00]
+addiu	$gp, $zero, 0   # CHECK: encoding: [0x24,0x1c,0x00,0x00]
+addiu	$sp, $zero, 0   # CHECK: encoding: [0x24,0x1d,0x00,0x00]
+addiu	$fp, $zero, 0   # CHECK: encoding: [0x24,0x1e,0x00,0x00]
+addiu	$s8, $zero, 0   # CHECK: encoding: [0x24,0x1e,0x00,0x00]
+addiu	$ra, $zero, 0   # CHECK: encoding: [0x24,0x1f,0x00,0x00]
Index: tools/llc/llc.cpp
===================================================================
--- tools/llc/llc.cpp
+++ tools/llc/llc.cpp
@@ -369,7 +369,15 @@
   Options.MCOptions.MCUseDwarfDirectory = EnableDwarfDirectory;
   Options.MCOptions.AsmVerbose = AsmVerbose;
   Options.MCOptions.PreserveAsmComments = PreserveComments;
-  Options.MCOptions.ABIName = MABI;
+
+  // Adjust the triple if we can. Otherwise put the ABI name in
+  // MCTargetOptions::ABIName.
+  std::tie(TheTriple, Options.MCOptions.ABIName) =
+      TheTriple.getABIVariant(MABI);
+  if (TheTriple.getArch() == Triple::UnknownArch) {
+    errs() << argv[0] << ": error: invalid -mabi value.\n";
+    return 1;
+  }
 
   std::unique_ptr<TargetMachine> Target(
       TheTarget->createTargetMachine(TheTriple.getTriple(), CPUStr, FeaturesStr,
Index: tools/llvm-mc/llvm-mc.cpp
===================================================================
--- tools/llvm-mc/llvm-mc.cpp
+++ tools/llvm-mc/llvm-mc.cpp
@@ -403,8 +403,16 @@
   if (!TheTarget)
     return 1;
 
+  // Adjust the triple if we can. Otherwise put the ABI name in
+  // MCTargetOptions::ABIName.
   MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
-  MCOptions.ABIName = MABI;
+  Triple TT;
+  std::tie(TT, MCOptions.ABIName) = Triple(TripleName).getABIVariant(MABI);
+  if (TT.getArch() == Triple::UnknownArch) {
+    errs() << ProgName << ": error: invalid -mabi value\n";
+    return 1;
+  }
+  TripleName = TT.str();
 
   // Now that GetTarget() has (potentially) replaced TripleName, it's safe to
   // construct the Triple object.
Index: unittests/ADT/TripleTest.cpp
===================================================================
--- unittests/ADT/TripleTest.cpp
+++ unittests/ADT/TripleTest.cpp
@@ -248,6 +248,42 @@
   EXPECT_EQ(Triple::AMDHSA, T.getOS());
   EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment());
 
+  T = Triple("mips-mti-linux-gnu");
+  EXPECT_EQ(Triple::mips, T.getArch());
+  EXPECT_EQ(Triple::MipsTechnologies, T.getVendor());
+  EXPECT_EQ(Triple::Linux, T.getOS());
+  EXPECT_EQ(Triple::GNU, T.getEnvironment());
+
+  T = Triple("mipsel-img-linux-gnu");
+  EXPECT_EQ(Triple::mipsel, T.getArch());
+  EXPECT_EQ(Triple::ImaginationTechnologies, T.getVendor());
+  EXPECT_EQ(Triple::Linux, T.getOS());
+  EXPECT_EQ(Triple::GNU, T.getEnvironment());
+
+  T = Triple("mips64-mti-linux-gnu");
+  EXPECT_EQ(Triple::mips64, T.getArch());
+  EXPECT_EQ(Triple::MipsTechnologies, T.getVendor());
+  EXPECT_EQ(Triple::Linux, T.getOS());
+  EXPECT_EQ(Triple::GNU, T.getEnvironment());
+
+  T = Triple("mips64el-img-linux-gnu");
+  EXPECT_EQ(Triple::mips64el, T.getArch());
+  EXPECT_EQ(Triple::ImaginationTechnologies, T.getVendor());
+  EXPECT_EQ(Triple::Linux, T.getOS());
+  EXPECT_EQ(Triple::GNU, T.getEnvironment());
+
+  T = Triple("mips64el-img-linux-gnuabin32");
+  EXPECT_EQ(Triple::mips64el, T.getArch());
+  EXPECT_EQ(Triple::ImaginationTechnologies, T.getVendor());
+  EXPECT_EQ(Triple::Linux, T.getOS());
+  EXPECT_EQ(Triple::GNUABIN32, T.getEnvironment());
+
+  T = Triple("mips64el-unknown-linux-gnuabi64");
+  EXPECT_EQ(Triple::mips64el, T.getArch());
+  EXPECT_EQ(Triple::UnknownVendor, T.getVendor());
+  EXPECT_EQ(Triple::Linux, T.getOS());
+  EXPECT_EQ(Triple::GNUABI64, T.getEnvironment());
+
   T = Triple("huh");
   EXPECT_EQ(Triple::UnknownArch, T.getArch());
 }
@@ -709,6 +745,155 @@
   EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch());
 }
 
+TEST(TripleTest, ABIVariants) {
+  Triple T, MutatedT;
+  StringRef MutatedABI;
+  EXPECT_EQ(Triple::UnknownArch, T.getABIVariant("").first.getArch());
+  EXPECT_EQ(Triple::UnknownArch, T.getABIVariant("32").first.getArch());
+
+  T.setArch(Triple::UnknownArch);
+  EXPECT_EQ(Triple::UnknownArch, T.getABIVariant("").first.getArch());
+  EXPECT_EQ(Triple::UnknownArch, T.getABIVariant("32").first.getArch());
+
+  // Try a triple that doesn't mutate the triple in getABIVariant().
+  // This should leave the triple and abi unchanged.
+  T = Triple("x86_64-pc-linux-gnu");
+  std::tie(MutatedT, MutatedABI) = T.getABIVariant("");
+  EXPECT_EQ(T, MutatedT);
+  EXPECT_EQ("", MutatedABI);
+
+  // Try an unsupported ABI name. This should change the arch component of the
+  // triple to UnknownArch. Other parts of the result are undefined.
+  std::tie(MutatedT, MutatedABI) = T.getABIVariant("32");
+  EXPECT_EQ(Triple::UnknownArch, MutatedT.getArch());
+
+  for (const auto &TripleStr :
+       {"mips-linux-gnu", "mipsel-linux-gnu", "mips-mti-linux-gnu",
+        "mipsel-img-linux-gnu", "mips64-linux-gnu", "mips64el-linux-gnu",
+        "mips64-mti-linux-gnu", "mips64el-img-linux-gnu"}) {
+    T = Triple(Triple::normalize(TripleStr));
+    std::pair<Triple, StringRef> Default = T.getABIVariant("");
+    std::pair<Triple, StringRef> O32 = T.getABIVariant("o32");
+    std::pair<Triple, StringRef> N32 = T.getABIVariant("n32");
+    std::pair<Triple, StringRef> N64 = T.getABIVariant("n64");
+
+    EXPECT_EQ(T.getArch(), Default.first.getArch());
+    EXPECT_EQ(T.get32BitArchVariant().getArch(), O32.first.getArch());
+    EXPECT_EQ(T.get64BitArchVariant().getArch(), N32.first.getArch());
+    EXPECT_EQ(T.get64BitArchVariant().getArch(), N64.first.getArch());
+
+    EXPECT_EQ(T.getVendor(), Default.first.getVendor());
+    EXPECT_EQ(T.getVendor(), O32.first.getVendor());
+    EXPECT_EQ(T.getVendor(), N32.first.getVendor());
+    EXPECT_EQ(T.getVendor(), N64.first.getVendor());
+
+    EXPECT_EQ(T.getOS(), Default.first.getOS());
+    EXPECT_EQ(T.getOS(), O32.first.getOS());
+    EXPECT_EQ(T.getOS(), N32.first.getOS());
+    EXPECT_EQ(T.getOS(), N64.first.getOS());
+
+    EXPECT_EQ((T.getArch() == Triple::mips64 || T.getArch() == Triple::mips64el)
+                  ? Triple::GNUABI64
+                  : Triple::GNUABI32,
+              Default.first.getEnvironment());
+    EXPECT_EQ(Triple::GNUABI32, O32.first.getEnvironment());
+    EXPECT_EQ(Triple::GNUABIN32, N32.first.getEnvironment());
+    EXPECT_EQ(Triple::GNUABI64, N64.first.getEnvironment());
+
+    EXPECT_EQ("", Default.second);
+    EXPECT_EQ("", O32.second);
+    EXPECT_EQ("", N32.second);
+    EXPECT_EQ("", N64.second);
+
+    // Try an unsupported ABI name. This should change the arch component of the
+    // triple to UnknownArch. Other values are undefined.
+    std::pair<Triple, StringRef> Foo = T.getABIVariant("foo");
+    EXPECT_EQ(Triple::UnknownArch, Foo.first.getArch());
+  }
+
+  for (const auto &TripleStr :
+       {"mipsel-linux-android", "mips64el-linux-android"}) {
+    T = Triple(Triple::normalize(TripleStr));
+    std::pair<Triple, StringRef> Default = T.getABIVariant("");
+    std::pair<Triple, StringRef> O32 = T.getABIVariant("o32");
+    std::pair<Triple, StringRef> N32 = T.getABIVariant("n32");
+    std::pair<Triple, StringRef> N64 = T.getABIVariant("n64");
+
+    EXPECT_EQ(T.getArch(), Default.first.getArch());
+    EXPECT_EQ(T.get32BitArchVariant().getArch(), O32.first.getArch());
+    EXPECT_EQ(T.get64BitArchVariant().getArch(), N64.first.getArch());
+
+    EXPECT_EQ(T.getVendor(), Default.first.getVendor());
+    EXPECT_EQ(T.getVendor(), O32.first.getVendor());
+    EXPECT_EQ(T.getVendor(), N64.first.getVendor());
+
+    EXPECT_EQ(T.getOS(), Default.first.getOS());
+    EXPECT_EQ(T.getOS(), O32.first.getOS());
+    EXPECT_EQ(T.getOS(), N64.first.getOS());
+
+    EXPECT_EQ((T.getArch() == Triple::mips64 || T.getArch() == Triple::mips64el)
+                  ? Triple::AndroidABI64
+                  : Triple::AndroidABI32,
+              Default.first.getEnvironment());
+    EXPECT_EQ(Triple::AndroidABI32, O32.first.getEnvironment());
+    EXPECT_EQ(Triple::AndroidABI64, N64.first.getEnvironment());
+
+    EXPECT_EQ("", Default.second);
+    EXPECT_EQ("", O32.second);
+    EXPECT_EQ("", N64.second);
+
+    // Try an unsupported ABI name. This should change the arch component of the
+    // triple to UnknownArch. Other values are undefined.
+    std::pair<Triple, StringRef> Foo = T.getABIVariant("foo");
+    EXPECT_EQ(Triple::UnknownArch, Foo.first.getArch());
+
+    // N32 is known to LLVM but unsupported for Android.
+    EXPECT_EQ(Triple::UnknownArch, N32.first.getArch());
+  }
+
+  for (const auto &TripleStr :
+       {"mips-linux", "mipsel-linux", "mips-mti-linux", "mipsel-img-linux",
+        "mips-unknown-freebsd", "mips64-linux", "mips64el-linux",
+        "mips64-mti-linux", "mips64el-img-linux", "mips64-unknown-freebsd"}) {
+    T = Triple(Triple::normalize(TripleStr));
+    std::pair<Triple, StringRef> Default = T.getABIVariant("");
+    std::pair<Triple, StringRef> O32 = T.getABIVariant("o32");
+    std::pair<Triple, StringRef> N32 = T.getABIVariant("n32");
+    std::pair<Triple, StringRef> N64 = T.getABIVariant("n64");
+    std::pair<Triple, StringRef> Foo = T.getABIVariant("foo");
+
+    EXPECT_EQ(T.getArch(), Default.first.getArch());
+    EXPECT_EQ(T.get32BitArchVariant().getArch(), O32.first.getArch());
+    EXPECT_EQ(T.get64BitArchVariant().getArch(), N32.first.getArch());
+    EXPECT_EQ(T.get64BitArchVariant().getArch(), N64.first.getArch());
+    EXPECT_EQ(Triple::UnknownArch, Foo.first.getArch());
+
+    EXPECT_EQ(T.getVendor(), Default.first.getVendor());
+    EXPECT_EQ(T.getVendor(), O32.first.getVendor());
+    EXPECT_EQ(T.getVendor(), N32.first.getVendor());
+    EXPECT_EQ(T.getVendor(), N64.first.getVendor());
+
+    EXPECT_EQ(T.getOS(), Default.first.getOS());
+    EXPECT_EQ(T.getOS(), O32.first.getOS());
+    EXPECT_EQ(T.getOS(), N32.first.getOS());
+    EXPECT_EQ(T.getOS(), N64.first.getOS());
+
+    EXPECT_EQ((T.getArch() == Triple::mips64 || T.getArch() == Triple::mips64el)
+                  ? Triple::ABI64
+                  : Triple::ABI32,
+              Default.first.getEnvironment());
+    EXPECT_EQ(Triple::ABI32, O32.first.getEnvironment());
+    EXPECT_EQ(Triple::ABIN32, N32.first.getEnvironment());
+    EXPECT_EQ(Triple::ABI64, N64.first.getEnvironment());
+
+    EXPECT_EQ("", Default.second);
+    EXPECT_EQ("", O32.second);
+    EXPECT_EQ("", N32.second);
+    EXPECT_EQ("", N64.second);
+    EXPECT_EQ("foo", Foo.second);
+  }
+}
+
 TEST(TripleTest, getOSVersion) {
   Triple T;
   unsigned Major, Minor, Micro;