Commit ade10387a5

Andrew Kelley <andrew@ziglang.org>
2019-02-26 21:51:32
breaking changes to the way targets work in zig
* CLI: `-target [name]` instead of `--target-*` args. This matches clang's API. * `builtin.Environ` renamed to `builtin.Abi` - likewise `builtin.environ` renamed to `builtin.abi` * stop hiding the concept of sub-arch. closes #1526 * `zig targets` only shows available targets. closes #438 * include all targets in readme, even those that don't print with `zig targets` but note they are Tier 4 * refactor target.cpp and make the naming conventions more consistent * introduce the concept of a "default C ABI" for a given OS/Arch combo. As a rule of thumb, if the system compiler is clang or gcc then the default C ABI is the gnu ABI.
1 parent 1ec1097
doc/docgen.zig
@@ -1105,14 +1105,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
                             },
                         }
                         if (code.target_windows) {
-                            try test_args.appendSlice([][]const u8{
-                                "--target-os",
-                                "windows",
-                                "--target-arch",
-                                "x86_64",
-                                "--target-environ",
-                                "msvc",
-                            });
+                            try test_args.appendSlice([][]const u8{ "-target", "x86_64-windows" });
                         }
                         const result = exec(allocator, &env_map, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed");
                         const escaped_stderr = try escapeHtml(allocator, result.stderr);
src/all_types.hpp
@@ -1784,7 +1784,8 @@ struct CodeGen {
     unsigned pointer_size_bytes;
     uint32_t target_os_index;
     uint32_t target_arch_index;
-    uint32_t target_environ_index;
+    uint32_t target_sub_arch_index;
+    uint32_t target_abi_index;
     uint32_t target_oformat_index;
     bool is_big_endian;
     bool have_pub_main;
src/analyze.cpp
@@ -1102,7 +1102,7 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
     if (type_is_c_abi_int(g, fn_type_id->return_type)) {
         return false;
     }
-    if (g->zig_target->arch.arch == ZigLLVM_x86_64) {
+    if (g->zig_target->arch == ZigLLVM_x86_64) {
         X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
         return abi_class == X64CABIClass_MEMORY;
     } else if (target_is_arm(g->zig_target)) {
src/buffer.hpp
@@ -132,9 +132,7 @@ void buf_appendf(Buf *buf, const char *format, ...)
 
 static inline bool buf_eql_mem(Buf *buf, const char *mem, size_t mem_len) {
     assert(buf->list.length);
-    if (buf_len(buf) != mem_len)
-        return false;
-    return memcmp(buf_ptr(buf), mem, mem_len) == 0;
+    return mem_eql_mem(buf_ptr(buf), buf_len(buf), mem, mem_len);
 }
 
 static inline bool buf_eql_str(Buf *buf, const char *str) {
src/codegen.cpp
@@ -29,9 +29,9 @@ static void init_darwin_native(CodeGen *g) {
 
     // Allow conflicts among OSX and iOS, but choose the default platform.
     if (osx_target && ios_target) {
-        if (g->zig_target->arch.arch == ZigLLVM_arm ||
-            g->zig_target->arch.arch == ZigLLVM_aarch64 ||
-            g->zig_target->arch.arch == ZigLLVM_thumb)
+        if (g->zig_target->arch == ZigLLVM_arm ||
+            g->zig_target->arch == ZigLLVM_aarch64 ||
+            g->zig_target->arch == ZigLLVM_thumb)
         {
             osx_target = nullptr;
         } else {
@@ -347,8 +347,8 @@ static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) {
         case CallingConventionC: return LLVMCCallConv;
         case CallingConventionCold:
             // cold calling convention only works on x86.
-            if (g->zig_target->arch.arch == ZigLLVM_x86 ||
-                g->zig_target->arch.arch == ZigLLVM_x86_64)
+            if (g->zig_target->arch == ZigLLVM_x86 ||
+                g->zig_target->arch == ZigLLVM_x86_64)
             {
                 // cold calling convention is not supported on windows
                 if (g->zig_target->os == OsWindows) {
@@ -364,7 +364,7 @@ static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) {
             zig_unreachable();
         case CallingConventionStdcall:
             // stdcall calling convention only works on x86.
-            if (g->zig_target->arch.arch == ZigLLVM_x86) {
+            if (g->zig_target->arch == ZigLLVM_x86) {
                 return LLVMX86StdcallCallConv;
             } else {
                 return LLVMCCallConv;
@@ -463,7 +463,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
     bool external_linkage = linkage != GlobalLinkageIdInternal;
     CallingConvention cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc;
     if (cc == CallingConventionStdcall && external_linkage &&
-        g->zig_target->arch.arch == ZigLLVM_x86)
+        g->zig_target->arch == ZigLLVM_x86)
     {
         // prevent llvm name mangling
         symbol_name = buf_sprintf("\x01_%s", buf_ptr(symbol_name));
@@ -2095,7 +2095,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
         return true;
     }
 
-    if (g->zig_target->arch.arch == ZigLLVM_x86_64) {
+    if (g->zig_target->arch == ZigLLVM_x86_64) {
         X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty);
         size_t ty_size = type_size(g, ty);
         if (abi_class == X64CABIClass_MEMORY) {
@@ -3344,9 +3344,9 @@ static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default
     LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
     bool asm_has_side_effects = true;
     bool asm_is_alignstack = false;
-    if (g->zig_target->arch.arch == ZigLLVM_x86_64) {
+    if (g->zig_target->arch == ZigLLVM_x86_64) {
         if (g->zig_target->os == OsLinux || target_is_darwin(g->zig_target) || g->zig_target->os == OsSolaris ||
-            (g->zig_target->os == OsWindows && g->zig_target->env_type != ZigLLVM_MSVC))
+            (g->zig_target->os == OsWindows && g->zig_target->abi != ZigLLVM_MSVC))
         {
             if (g->cur_fn->valgrind_client_request_array == nullptr) {
                 LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
@@ -3586,7 +3586,7 @@ static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) {
     LLVMValueRef write_register_fn_val = get_write_register_fn_val(g);
 
     if (g->sp_md_node == nullptr) {
-        Buf *sp_reg_name = buf_create_from_str(arch_stack_pointer_register_name(&g->zig_target->arch));
+        Buf *sp_reg_name = buf_create_from_str(arch_stack_pointer_register_name(g->zig_target->arch));
         LLVMValueRef str_node = LLVMMDString(buf_ptr(sp_reg_name), buf_len(sp_reg_name) + 1);
         g->sp_md_node = LLVMMDNode(&str_node, 1);
     }
@@ -7284,8 +7284,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
         buf_appendf(contents, "pub const Os = enum {\n");
         uint32_t field_count = (uint32_t)target_os_count();
         for (uint32_t i = 0; i < field_count; i += 1) {
-            Os os_type = get_target_os(i);
-            const char *name = get_target_os_name(os_type);
+            Os os_type = target_os_enum(i);
+            const char *name = target_os_name(os_type);
             buf_appendf(contents, "    %s,\n", name);
 
             if (os_type == g->zig_target->os) {
@@ -7299,53 +7299,77 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
 
     const char *cur_arch = nullptr;
     {
-        buf_appendf(contents, "pub const Arch = enum {\n");
+        buf_appendf(contents, "pub const Arch = union(enum) {\n");
         uint32_t field_count = (uint32_t)target_arch_count();
-        for (uint32_t i = 0; i < field_count; i += 1) {
-            const ArchType *arch_type = get_target_arch(i);
-            Buf *arch_name = buf_alloc();
-            buf_resize(arch_name, 50);
-            get_arch_name(buf_ptr(arch_name), arch_type);
-            buf_resize(arch_name, strlen(buf_ptr(arch_name)));
-
-            buf_appendf(contents, "    %s,\n", buf_ptr(arch_name));
+        for (uint32_t arch_i = 0; arch_i < field_count; arch_i += 1) {
+            ZigLLVM_ArchType arch = target_arch_enum(arch_i);
+            const char *arch_name = target_arch_name(arch);
+            SubArchList sub_arch_list = target_subarch_list(arch);
+            if (sub_arch_list == SubArchListNone) {
+                buf_appendf(contents, "    %s,\n", arch_name);
+                if (arch == g->zig_target->arch) {
+                    g->target_arch_index = arch_i;
+                    cur_arch = buf_ptr(buf_sprintf("Arch.%s", arch_name));
+                }
+            } else {
+                const char *sub_arch_list_name = target_subarch_list_name(sub_arch_list);
+                buf_appendf(contents, "    %s: %s,\n", arch_name, sub_arch_list_name);
+                if (arch == g->zig_target->arch) {
+                    size_t sub_count = target_subarch_count(sub_arch_list);
+                    for (size_t sub_i = 0; sub_i < sub_count; sub_i += 1) {
+                        ZigLLVM_SubArchType sub = target_subarch_enum(sub_arch_list, sub_i);
+                        if (sub == g->zig_target->sub_arch) {
+                            g->target_sub_arch_index = sub_i;
+                            cur_arch = buf_ptr(buf_sprintf("Arch{ .%s = Arch.%s.%s }",
+                                        arch_name, sub_arch_list_name, target_subarch_name(sub)));
+                        }
+                    }
+                }
+            }
+        }
 
-            if (arch_type->arch == g->zig_target->arch.arch &&
-                arch_type->sub_arch == g->zig_target->arch.sub_arch)
-            {
-                g->target_arch_index = i;
-                cur_arch = buf_ptr(arch_name);
+        uint32_t list_count = target_subarch_list_count();
+        // start at index 1 to skip None
+        for (uint32_t list_i = 1; list_i < list_count; list_i += 1) {
+            SubArchList sub_arch_list = target_subarch_list_enum(list_i);
+            const char *subarch_list_name = target_subarch_list_name(sub_arch_list);
+            buf_appendf(contents, "    pub const %s = enum {\n", subarch_list_name);
+            size_t sub_count = target_subarch_count(sub_arch_list);
+            for (size_t sub_i = 0; sub_i < sub_count; sub_i += 1) {
+                ZigLLVM_SubArchType sub = target_subarch_enum(sub_arch_list, sub_i);
+                buf_appendf(contents, "        %s,\n", target_subarch_name(sub));
             }
+            buf_appendf(contents, "    };\n");
         }
         buf_appendf(contents, "};\n\n");
     }
     assert(cur_arch != nullptr);
 
-    const char *cur_environ = nullptr;
+    const char *cur_abi = nullptr;
     {
-        buf_appendf(contents, "pub const Environ = enum {\n");
-        uint32_t field_count = (uint32_t)target_environ_count();
+        buf_appendf(contents, "pub const Abi = enum {\n");
+        uint32_t field_count = (uint32_t)target_abi_count();
         for (uint32_t i = 0; i < field_count; i += 1) {
-            ZigLLVM_EnvironmentType environ_type = get_target_environ(i);
-            const char *name = ZigLLVMGetEnvironmentTypeName(environ_type);
+            ZigLLVM_EnvironmentType abi = target_abi_enum(i);
+            const char *name = target_abi_name(abi);
             buf_appendf(contents, "    %s,\n", name);
 
-            if (environ_type == g->zig_target->env_type) {
-                g->target_environ_index = i;
-                cur_environ = name;
+            if (abi == g->zig_target->abi) {
+                g->target_abi_index = i;
+                cur_abi = name;
             }
         }
         buf_appendf(contents, "};\n\n");
     }
-    assert(cur_environ != nullptr);
+    assert(cur_abi != nullptr);
 
     const char *cur_obj_fmt = nullptr;
     {
         buf_appendf(contents, "pub const ObjectFormat = enum {\n");
         uint32_t field_count = (uint32_t)target_oformat_count();
         for (uint32_t i = 0; i < field_count; i += 1) {
-            ZigLLVM_ObjectFormatType oformat = get_target_oformat(i);
-            const char *name = get_target_oformat_name(oformat);
+            ZigLLVM_ObjectFormatType oformat = target_oformat_enum(i);
+            const char *name = target_oformat_name(oformat);
             buf_appendf(contents, "    %s,\n", name);
 
             ZigLLVM_ObjectFormatType target_oformat = target_object_format(g->zig_target);
@@ -7633,8 +7657,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
     buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
     buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
     buf_appendf(contents, "pub const os = Os.%s;\n", cur_os);
-    buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch);
-    buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ);
+    buf_appendf(contents, "pub const arch = %s;\n", cur_arch);
+    buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi);
     buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt);
     buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
     buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));
@@ -7669,11 +7693,11 @@ static Error define_builtin_compile_vars(CodeGen *g) {
     cache_bool(&cache_hash, g->is_test_build);
     cache_bool(&cache_hash, g->is_single_threaded);
     cache_int(&cache_hash, g->zig_target->is_native);
-    cache_int(&cache_hash, g->zig_target->arch.arch);
-    cache_int(&cache_hash, g->zig_target->arch.sub_arch);
+    cache_int(&cache_hash, g->zig_target->arch);
+    cache_int(&cache_hash, g->zig_target->sub_arch);
     cache_int(&cache_hash, g->zig_target->vendor);
     cache_int(&cache_hash, g->zig_target->os);
-    cache_int(&cache_hash, g->zig_target->env_type);
+    cache_int(&cache_hash, g->zig_target->abi);
     cache_bool(&cache_hash, g->have_err_ret_tracing);
     cache_bool(&cache_hash, g->libc_link_lib != nullptr);
     cache_bool(&cache_hash, g->valgrind_support);
@@ -7901,7 +7925,7 @@ static void detect_libc(CodeGen *g) {
         // without a cross compiling libc kit.
         fprintf(stderr,
             "Cannot link against libc for non-native OS '%s' without providing a libc installation file.\n"
-            "See `zig libc --help` for more details.\n", get_target_os_name(g->zig_target->os));
+            "See `zig libc --help` for more details.\n", target_os_name(g->zig_target->os));
         exit(1);
     }
 }
@@ -8799,11 +8823,11 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
     cache_int(ch, g->build_mode);
     cache_int(ch, g->out_type);
     cache_bool(ch, g->zig_target->is_native);
-    cache_int(ch, g->zig_target->arch.arch);
-    cache_int(ch, g->zig_target->arch.sub_arch);
+    cache_int(ch, g->zig_target->arch);
+    cache_int(ch, g->zig_target->sub_arch);
     cache_int(ch, g->zig_target->vendor);
     cache_int(ch, g->zig_target->os);
-    cache_int(ch, g->zig_target->env_type);
+    cache_int(ch, g->zig_target->abi);
     cache_int(ch, g->subsystem);
     cache_bool(ch, g->is_static);
     cache_bool(ch, g->strip_debug_symbols);
src/error.cpp
@@ -37,6 +37,11 @@ const char *err_str(Error err) {
         case ErrorPathTooLong: return "path too long";
         case ErrorCCompilerCannotFindFile: return "C compiler cannot find file";
         case ErrorReadingDepFile: return "failed to read .d file";
+        case ErrorMissingArchitecture: return "missing architecture";
+        case ErrorMissingOperatingSystem: return "missing operating system";
+        case ErrorUnknownArchitecture: return "unrecognized architecture";
+        case ErrorUnknownOperatingSystem: return "unrecognized operating system";
+        case ErrorUnknownABI: return "unrecognized C ABI";
     }
     return "(invalid error)";
 }
src/error.hpp
@@ -39,6 +39,11 @@ enum Error {
     ErrorPathTooLong,
     ErrorCCompilerCannotFindFile,
     ErrorReadingDepFile,
+    ErrorMissingArchitecture,
+    ErrorMissingOperatingSystem,
+    ErrorUnknownArchitecture,
+    ErrorUnknownOperatingSystem,
+    ErrorUnknownABI,
 };
 
 const char *err_str(Error err);
src/libc_installation.cpp
@@ -103,7 +103,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
     if (buf_len(&libc->crt_dir) == 0) {
         if (!target_is_darwin(target)) {
             if (verbose) {
-                fprintf(stderr, "crt_dir may not be empty for %s\n", get_target_os_name(target->os));
+                fprintf(stderr, "crt_dir may not be empty for %s\n", target_os_name(target->os));
             }
             return ErrorSemanticAnalyzeFail;
         }
@@ -112,7 +112,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
     if (buf_len(&libc->lib_dir) == 0) {
         if (!target_is_darwin(target) && target->os != OsWindows) {
             if (verbose) {
-                fprintf(stderr, "lib_dir may not be empty for %s\n", get_target_os_name(target->os));
+                fprintf(stderr, "lib_dir may not be empty for %s\n", target_os_name(target->os));
             }
             return ErrorSemanticAnalyzeFail;
         }
@@ -121,7 +121,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
     if (buf_len(&libc->static_lib_dir) == 0) {
         if (!target_is_darwin(target) && target->os != OsWindows) {
             if (verbose) {
-                fprintf(stderr, "static_lib_dir may not be empty for %s\n", get_target_os_name(target->os));
+                fprintf(stderr, "static_lib_dir may not be empty for %s\n", target_os_name(target->os));
             }
             return ErrorSemanticAnalyzeFail;
         }
@@ -130,7 +130,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
     if (buf_len(&libc->msvc_lib_dir) == 0) {
         if (target->os == OsWindows) {
             if (verbose) {
-                fprintf(stderr, "msvc_lib_dir may not be empty for %s\n", get_target_os_name(target->os));
+                fprintf(stderr, "msvc_lib_dir may not be empty for %s\n", target_os_name(target->os));
             }
             return ErrorSemanticAnalyzeFail;
         }
@@ -139,7 +139,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
     if (buf_len(&libc->kernel32_lib_dir) == 0) {
         if (target->os == OsWindows) {
             if (verbose) {
-                fprintf(stderr, "kernel32_lib_dir may not be empty for %s\n", get_target_os_name(target->os));
+                fprintf(stderr, "kernel32_lib_dir may not be empty for %s\n", target_os_name(target->os));
             }
             return ErrorSemanticAnalyzeFail;
         }
@@ -148,7 +148,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
     if (buf_len(&libc->dynamic_linker_path) == 0) {
         if (!target_is_darwin(target) && target->os != OsWindows) {
             if (verbose) {
-                fprintf(stderr, "dynamic_linker_path may not be empty for %s\n", get_target_os_name(target->os));
+                fprintf(stderr, "dynamic_linker_path may not be empty for %s\n", target_os_name(target->os));
             }
             return ErrorSemanticAnalyzeFail;
         }
@@ -334,8 +334,10 @@ static Error zig_libc_find_native_dynamic_linker_posix(ZigLibCInstallation *self
 #endif
     ZigTarget native_target;
     get_native_target(&native_target);
-    Buf *dynamic_linker_path = target_dynamic_linker(&native_target);
-    buf_init_from_buf(&self->dynamic_linker_path, dynamic_linker_path);
+    const char *dynamic_linker_path = target_dynamic_linker(&native_target);
+    if (dynamic_linker_path != nullptr) {
+        buf_init_from_str(&self->dynamic_linker_path, dynamic_linker_path);
+    }
     return ErrorNone;
 }
 #endif
src/link.cpp
@@ -96,7 +96,7 @@ static Buf *build_compiler_rt(CodeGen *parent_gen) {
 }
 
 static const char *get_darwin_arch_string(const ZigTarget *t) {
-    switch (t->arch.arch) {
+    switch (t->arch) {
         case ZigLLVM_aarch64:
             return "arm64";
         case ZigLLVM_thumb:
@@ -109,13 +109,13 @@ static const char *get_darwin_arch_string(const ZigTarget *t) {
         case ZigLLVM_ppc64le:
             return "ppc64le";
         default:
-            return ZigLLVMGetArchTypeName(t->arch.arch);
+            return ZigLLVMGetArchTypeName(t->arch);
     }
 }
 
 
 static const char *getLDMOption(const ZigTarget *t) {
-    switch (t->arch.arch) {
+    switch (t->arch) {
         case ZigLLVM_x86:
             return "elf_i386";
         case ZigLLVM_aarch64:
@@ -149,7 +149,7 @@ static const char *getLDMOption(const ZigTarget *t) {
         case ZigLLVM_systemz:
             return "elf64_s390";
         case ZigLLVM_x86_64:
-            if (t->env_type == ZigLLVM_GNUX32) {
+            if (t->abi == ZigLLVM_GNUX32) {
                 return "elf32_x86_64";
             }
             // Any target elf will use the freebsd osabi if suffixed with "_fbsd".
@@ -191,8 +191,8 @@ static void construct_linker_job_elf(LinkJob *lj) {
     bool shared = !g->is_static && is_lib;
     Buf *soname = nullptr;
     if (g->is_static) {
-        if (g->zig_target->arch.arch == ZigLLVM_arm || g->zig_target->arch.arch == ZigLLVM_armeb ||
-            g->zig_target->arch.arch == ZigLLVM_thumb || g->zig_target->arch.arch == ZigLLVM_thumbeb)
+        if (g->zig_target->arch == ZigLLVM_arm || g->zig_target->arch == ZigLLVM_armeb ||
+            g->zig_target->arch == ZigLLVM_thumb || g->zig_target->arch == ZigLLVM_thumbeb)
         {
             lj->args.append("-Bstatic");
         } else {
@@ -375,16 +375,16 @@ static void construct_linker_job_wasm(LinkJob *lj) {
 }
 
 //static bool is_target_cyg_mingw(const ZigTarget *target) {
-//    return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) ||
-//        (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU);
+//    return (target->os == ZigLLVM_Win32 && target->abi == ZigLLVM_Cygnus) ||
+//        (target->os == ZigLLVM_Win32 && target->abi == ZigLLVM_GNU);
 //}
 
 static void coff_append_machine_arg(CodeGen *g, ZigList<const char *> *list) {
-    if (g->zig_target->arch.arch == ZigLLVM_x86) {
+    if (g->zig_target->arch == ZigLLVM_x86) {
         list->append("-MACHINE:X86");
-    } else if (g->zig_target->arch.arch == ZigLLVM_x86_64) {
+    } else if (g->zig_target->arch == ZigLLVM_x86_64) {
         list->append("-MACHINE:X64");
-    } else if (g->zig_target->arch.arch == ZigLLVM_arm) {
+    } else if (g->zig_target->arch == ZigLLVM_arm) {
         list->append("-MACHINE:ARM");
     }
 }
@@ -470,7 +470,7 @@ static void add_nt_link_args(LinkJob *lj, bool is_library) {
 //    lj->args.append("-Bdynamic");
 //    if (dll || shared) {
 //        lj->args.append("-e");
-//        if (g->zig_target.arch.arch == ZigLLVM_x86) {
+//        if (g->zig_target.arch == ZigLLVM_x86) {
 //            lj->args.append("_DllMainCRTStartup@12");
 //        } else {
 //            lj->args.append("DllMainCRTStartup");
@@ -496,7 +496,7 @@ static void add_nt_link_args(LinkJob *lj, bool is_library) {
 //lj->args.append("-lmingw32");
 
 //lj->args.append("-lgcc");
-//bool is_android = (g->zig_target.env_type == ZigLLVM_Android);
+//bool is_android = (g->zig_target.abi == ZigLLVM_Android);
 //bool is_cyg_ming = is_target_cyg_mingw(&g->zig_target);
 //if (!g->is_static && !is_android) {
 //    if (!is_cyg_ming) {
@@ -637,7 +637,7 @@ static void construct_linker_job_coff(LinkJob *lj) {
             continue;
         }
         if (link_lib->provided_explicitly) {
-            if (lj->codegen->zig_target->env_type == ZigLLVM_GNU) {
+            if (lj->codegen->zig_target->abi == ZigLLVM_GNU) {
                 Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
                 lj->args.append(buf_ptr(arg));
             }
@@ -764,8 +764,8 @@ static void get_darwin_platform(LinkJob *lj, DarwinPlatform *platform) {
     }
 
     if (platform->kind == IPhoneOS &&
-        (g->zig_target->arch.arch == ZigLLVM_x86 ||
-         g->zig_target->arch.arch == ZigLLVM_x86_64))
+        (g->zig_target->arch == ZigLLVM_x86 ||
+         g->zig_target->arch == ZigLLVM_x86_64))
     {
         platform->kind = IPhoneOSSimulator;
     }
@@ -886,7 +886,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
                 }
                 break;
             case IPhoneOS:
-                if (g->zig_target->arch.arch == ZigLLVM_aarch64) {
+                if (g->zig_target->arch == ZigLLVM_aarch64) {
                     // iOS does not need any crt1 files for arm64
                 } else if (darwin_version_lt(&platform, 3, 1)) {
                     lj->args.append("-lcrt1.o");
src/main.cpp
@@ -69,9 +69,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
         "  --single-threaded            source may assume it is only used single-threaded\n"
         "  --static                     output will be statically linked\n"
         "  --strip                      exclude debug symbols\n"
-        "  --target-arch [name]         specify target architecture\n"
-        "  --target-environ [name]      specify target environment\n"
-        "  --target-os [name]           specify target operating system\n"
+        "  -target [name]               <arch><sub>-<os>-<abi> see the targets command\n"
         "  --verbose-tokenize           enable compiler debug output for tokenization\n"
         "  --verbose-ast                enable compiler debug output for AST parsing\n"
         "  --verbose-link               enable compiler debug output for linking\n"
@@ -145,6 +143,14 @@ static const char *ZIG_ZEN = "\n"
 " * Minimize energy spent on coding style.\n"
 " * Together we serve end users.\n";
 
+static bool arch_available_in_llvm(ZigLLVM_ArchType arch) {
+    LLVMTargetRef target_ref;
+    char *err_msg = nullptr;
+    char triple_string[128];
+    sprintf(triple_string, "%s-unknown-unknown-unknown", ZigLLVMGetArchTypeName(arch));
+    return !LLVMGetTargetFromTriple(triple_string, &target_ref, &err_msg);
+}
+
 static int print_target_list(FILE *f) {
     ZigTarget native;
     get_native_target(&native);
@@ -152,28 +158,36 @@ static int print_target_list(FILE *f) {
     fprintf(f, "Architectures:\n");
     size_t arch_count = target_arch_count();
     for (size_t arch_i = 0; arch_i < arch_count; arch_i += 1) {
-        const ArchType *arch = get_target_arch(arch_i);
-        char arch_name[50];
-        get_arch_name(arch_name, arch);
-        const char *native_str = (native.arch.arch == arch->arch && native.arch.sub_arch == arch->sub_arch) ?
-            " (native)" : "";
-        fprintf(f, "  %s%s\n", arch_name, native_str);
+        ZigLLVM_ArchType arch = target_arch_enum(arch_i);
+        if (!arch_available_in_llvm(arch))
+            continue;
+        const char *arch_name = target_arch_name(arch);
+        SubArchList sub_arch_list = target_subarch_list(arch);
+        size_t sub_count = target_subarch_count(sub_arch_list);
+        const char *arch_native_str = (native.arch == arch) ? " (native)" : "";
+        fprintf(stderr, "  %s%s\n", arch_name, arch_native_str);
+        for (size_t sub_i = 0; sub_i < sub_count; sub_i += 1) {
+            ZigLLVM_SubArchType sub = target_subarch_enum(sub_arch_list, sub_i);
+            const char *sub_name = target_subarch_name(sub);
+            const char *sub_native_str = (native.arch == arch && native.sub_arch == sub) ? " (native)" : "";
+            fprintf(f, "    %s%s\n", sub_name, sub_native_str);
+        }
     }
 
     fprintf(f, "\nOperating Systems:\n");
     size_t os_count = target_os_count();
     for (size_t i = 0; i < os_count; i += 1) {
-        Os os_type = get_target_os(i);
+        Os os_type = target_os_enum(i);
         const char *native_str = (native.os == os_type) ? " (native)" : "";
-        fprintf(f, "  %s%s\n", get_target_os_name(os_type), native_str);
+        fprintf(f, "  %s%s\n", target_os_name(os_type), native_str);
     }
 
-    fprintf(f, "\nEnvironments:\n");
-    size_t environ_count = target_environ_count();
-    for (size_t i = 0; i < environ_count; i += 1) {
-        ZigLLVM_EnvironmentType environ_type = get_target_environ(i);
-        const char *native_str = (native.env_type == environ_type) ? " (native)" : "";
-        fprintf(f, "  %s%s\n", ZigLLVMGetEnvironmentTypeName(environ_type), native_str);
+    fprintf(f, "\nC ABIs:\n");
+    size_t abi_count = target_abi_count();
+    for (size_t i = 0; i < abi_count; i += 1) {
+        ZigLLVM_EnvironmentType abi = target_abi_enum(i);
+        const char *native_str = (native.abi == abi) ? " (native)" : "";
+        fprintf(f, "  %s%s\n", target_abi_name(abi), native_str);
     }
 
     return EXIT_SUCCESS;
@@ -397,9 +411,7 @@ int main(int argc, char **argv) {
     ZigList<const char *> link_libs = {0};
     ZigList<const char *> forbidden_link_libs = {0};
     ZigList<const char *> frameworks = {0};
-    const char *target_arch = nullptr;
-    const char *target_os = nullptr;
-    const char *target_environ = nullptr;
+    const char *target_string = nullptr;
     bool rdynamic = false;
     const char *mmacosx_version_min = nullptr;
     const char *mios_version_min = nullptr;
@@ -740,12 +752,8 @@ int main(int argc, char **argv) {
                     asm_files.append(argv[i]);
                 } else if (strcmp(arg, "--cache-dir") == 0) {
                     cache_dir = argv[i];
-                } else if (strcmp(arg, "--target-arch") == 0) {
-                    target_arch = argv[i];
-                } else if (strcmp(arg, "--target-os") == 0) {
-                    target_os = argv[i];
-                } else if (strcmp(arg, "--target-environ") == 0) {
-                    target_environ = argv[i];
+                } else if (strcmp(arg, "-target") == 0) {
+                    target_string = argv[i];
                 } else if (strcmp(arg, "-mmacosx-version-min") == 0) {
                     mmacosx_version_min = argv[i];
                 } else if (strcmp(arg, "-mios-version-min") == 0) {
@@ -874,27 +882,12 @@ int main(int argc, char **argv) {
     init_all_targets();
 
     ZigTarget target;
-    if (!target_arch && !target_os && !target_environ) {
+    if (target_string == nullptr) {
         get_native_target(&target);
     } else {
-        get_unknown_target(&target);
-        if (target_arch) {
-            if (parse_target_arch(target_arch, &target.arch)) {
-                fprintf(stderr, "invalid --target-arch argument\n");
-                return print_error_usage(arg0);
-            }
-        }
-        if (target_os) {
-            if (parse_target_os(target_os, &target.os)) {
-                fprintf(stderr, "invalid --target-os argument\n");
-                return print_error_usage(arg0);
-            }
-        }
-        if (target_environ) {
-            if (parse_target_environ(target_environ, &target.env_type)) {
-                fprintf(stderr, "invalid --target-environ argument\n");
-                return print_error_usage(arg0);
-            }
+        if ((err = target_parse_triple(&target, target_string))) {
+            fprintf(stderr, "invalid target: %s\n", err_str(err));
+            return print_error_usage(arg0);
         }
     }
 
src/target.cpp
@@ -13,115 +13,105 @@
 
 #include <stdio.h>
 
-static const ArchType arch_list[] = {
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8_3a},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8_2a},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8_1a},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8r},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8m_baseline},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8m_mainline},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7em},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7m},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7s},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7k},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7ve},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v6},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v6m},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v6k},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v6t2},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v5},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v5te},
-    {ZigLLVM_arm, ZigLLVM_ARMSubArch_v4t},
-
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8_3a},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8_2a},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8_1a},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8r},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8m_baseline},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8m_mainline},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7em},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7m},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7s},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7k},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7ve},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v6},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v6m},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v6k},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v6t2},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v5},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v5te},
-    {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v4t},
-
-    {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8_3a},
-    {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8_2a},
-    {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8_1a},
-    {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8},
-    {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8r},
-    {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8m_baseline},
-    {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8m_mainline},
-
-    {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8_3a},
-    {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8_2a},
-    {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8_1a},
-    {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8},
-    {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8r},
-    {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8m_baseline},
-    {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8m_mainline},
-
-    {ZigLLVM_arc, ZigLLVM_NoSubArch},
-    {ZigLLVM_avr, ZigLLVM_NoSubArch},
-    {ZigLLVM_bpfel, ZigLLVM_NoSubArch},
-    {ZigLLVM_bpfeb, ZigLLVM_NoSubArch},
-    {ZigLLVM_hexagon, ZigLLVM_NoSubArch},
-    {ZigLLVM_mips, ZigLLVM_NoSubArch},
-    {ZigLLVM_mipsel, ZigLLVM_NoSubArch},
-    {ZigLLVM_mips64, ZigLLVM_NoSubArch},
-    {ZigLLVM_mips64el, ZigLLVM_NoSubArch},
-    {ZigLLVM_msp430, ZigLLVM_NoSubArch},
-    {ZigLLVM_nios2, ZigLLVM_NoSubArch},
-    {ZigLLVM_ppc, ZigLLVM_NoSubArch},
-    {ZigLLVM_ppc64, ZigLLVM_NoSubArch},
-    {ZigLLVM_ppc64le, ZigLLVM_NoSubArch},
-    {ZigLLVM_r600, ZigLLVM_NoSubArch},
-    {ZigLLVM_amdgcn, ZigLLVM_NoSubArch},
-    {ZigLLVM_riscv32, ZigLLVM_NoSubArch},
-    {ZigLLVM_riscv64, ZigLLVM_NoSubArch},
-    {ZigLLVM_sparc, ZigLLVM_NoSubArch},
-    {ZigLLVM_sparcv9, ZigLLVM_NoSubArch},
-    {ZigLLVM_sparcel, ZigLLVM_NoSubArch},
-    {ZigLLVM_systemz, ZigLLVM_NoSubArch},
-    {ZigLLVM_tce, ZigLLVM_NoSubArch},
-    {ZigLLVM_tcele, ZigLLVM_NoSubArch},
-    {ZigLLVM_thumb, ZigLLVM_NoSubArch},
-    {ZigLLVM_thumbeb, ZigLLVM_NoSubArch},
-    {ZigLLVM_x86, ZigLLVM_NoSubArch},
-    {ZigLLVM_x86_64, ZigLLVM_NoSubArch},
-    {ZigLLVM_xcore, ZigLLVM_NoSubArch},
-    {ZigLLVM_nvptx, ZigLLVM_NoSubArch},
-    {ZigLLVM_nvptx64, ZigLLVM_NoSubArch},
-    {ZigLLVM_le32, ZigLLVM_NoSubArch},
-    {ZigLLVM_le64, ZigLLVM_NoSubArch},
-    {ZigLLVM_amdil, ZigLLVM_NoSubArch},
-    {ZigLLVM_amdil64, ZigLLVM_NoSubArch},
-    {ZigLLVM_hsail, ZigLLVM_NoSubArch},
-    {ZigLLVM_hsail64, ZigLLVM_NoSubArch},
-    {ZigLLVM_spir, ZigLLVM_NoSubArch},
-    {ZigLLVM_spir64, ZigLLVM_NoSubArch},
-
-    {ZigLLVM_kalimba, ZigLLVM_KalimbaSubArch_v3},
-    {ZigLLVM_kalimba, ZigLLVM_KalimbaSubArch_v4},
-    {ZigLLVM_kalimba, ZigLLVM_KalimbaSubArch_v5},
-
-    {ZigLLVM_shave, ZigLLVM_NoSubArch},
-    {ZigLLVM_lanai, ZigLLVM_NoSubArch},
-    {ZigLLVM_wasm32, ZigLLVM_NoSubArch},
-    {ZigLLVM_wasm64, ZigLLVM_NoSubArch},
-    {ZigLLVM_renderscript32, ZigLLVM_NoSubArch},
-    {ZigLLVM_renderscript64, ZigLLVM_NoSubArch},
+static const SubArchList subarch_list_list[] = {
+    SubArchListNone,
+    SubArchListArm32,
+    SubArchListArm64,
+    SubArchListKalimba,
+};
+
+static const ZigLLVM_SubArchType subarch_list_arm32[] = {
+    ZigLLVM_ARMSubArch_v8_4a,
+    ZigLLVM_ARMSubArch_v8_3a,
+    ZigLLVM_ARMSubArch_v8_2a,
+    ZigLLVM_ARMSubArch_v8_1a,
+    ZigLLVM_ARMSubArch_v8,
+    ZigLLVM_ARMSubArch_v8r,
+    ZigLLVM_ARMSubArch_v8m_baseline,
+    ZigLLVM_ARMSubArch_v8m_mainline,
+    ZigLLVM_ARMSubArch_v7,
+    ZigLLVM_ARMSubArch_v7em,
+    ZigLLVM_ARMSubArch_v7m,
+    ZigLLVM_ARMSubArch_v7s,
+    ZigLLVM_ARMSubArch_v7k,
+    ZigLLVM_ARMSubArch_v7ve,
+    ZigLLVM_ARMSubArch_v6,
+    ZigLLVM_ARMSubArch_v6m,
+    ZigLLVM_ARMSubArch_v6k,
+    ZigLLVM_ARMSubArch_v6t2,
+    ZigLLVM_ARMSubArch_v5,
+    ZigLLVM_ARMSubArch_v5te,
+    ZigLLVM_ARMSubArch_v4t,
+};
+
+static const ZigLLVM_SubArchType subarch_list_arm64[] = {
+    ZigLLVM_ARMSubArch_v8_4a,
+    ZigLLVM_ARMSubArch_v8_3a,
+    ZigLLVM_ARMSubArch_v8_2a,
+    ZigLLVM_ARMSubArch_v8_1a,
+    ZigLLVM_ARMSubArch_v8,
+    ZigLLVM_ARMSubArch_v8r,
+    ZigLLVM_ARMSubArch_v8m_baseline,
+    ZigLLVM_ARMSubArch_v8m_mainline,
+};
+
+static const ZigLLVM_SubArchType subarch_list_kalimba[] = {
+    ZigLLVM_KalimbaSubArch_v5,
+    ZigLLVM_KalimbaSubArch_v4,
+    ZigLLVM_KalimbaSubArch_v3,
+};
+
+static const ZigLLVM_ArchType arch_list[] = {
+    ZigLLVM_arm,
+    ZigLLVM_armeb,
+    ZigLLVM_aarch64,
+    ZigLLVM_aarch64_be,
+    ZigLLVM_arc,
+    ZigLLVM_avr,
+    ZigLLVM_bpfel,
+    ZigLLVM_bpfeb,
+    ZigLLVM_hexagon,
+    ZigLLVM_mips,
+    ZigLLVM_mipsel,
+    ZigLLVM_mips64,
+    ZigLLVM_mips64el,
+    ZigLLVM_msp430,
+    ZigLLVM_nios2,
+    ZigLLVM_ppc,
+    ZigLLVM_ppc64,
+    ZigLLVM_ppc64le,
+    ZigLLVM_r600,
+    ZigLLVM_amdgcn,
+    ZigLLVM_riscv32,
+    ZigLLVM_riscv64,
+    ZigLLVM_sparc,
+    ZigLLVM_sparcv9,
+    ZigLLVM_sparcel,
+    ZigLLVM_systemz,
+    ZigLLVM_tce,
+    ZigLLVM_tcele,
+    ZigLLVM_thumb,
+    ZigLLVM_thumbeb,
+    ZigLLVM_x86,
+    ZigLLVM_x86_64,
+    ZigLLVM_xcore,
+    ZigLLVM_nvptx,
+    ZigLLVM_nvptx64,
+    ZigLLVM_le32,
+    ZigLLVM_le64,
+    ZigLLVM_amdil,
+    ZigLLVM_amdil64,
+    ZigLLVM_hsail,
+    ZigLLVM_hsail64,
+    ZigLLVM_spir,
+    ZigLLVM_spir64,
+    ZigLLVM_kalimba,
+    ZigLLVM_shave,
+    ZigLLVM_lanai,
+    ZigLLVM_wasm32,
+    ZigLLVM_wasm64,
+    ZigLLVM_renderscript32,
+    ZigLLVM_renderscript64,
 };
 
 static const ZigLLVM_VendorType vendor_list[] = {
@@ -179,7 +169,7 @@ static const Os os_list[] = {
 };
 
 // Coordinate with zig_llvm.h
-static const ZigLLVM_EnvironmentType environ_list[] = {
+static const ZigLLVM_EnvironmentType abi_list[] = {
     ZigLLVM_UnknownEnvironment,
 
     ZigLLVM_GNU,
@@ -214,11 +204,12 @@ size_t target_oformat_count(void) {
     return array_length(oformat_list);
 }
 
-ZigLLVM_ObjectFormatType get_target_oformat(size_t index) {
+ZigLLVM_ObjectFormatType target_oformat_enum(size_t index) {
+    assert(index < array_length(oformat_list));
     return oformat_list[index];
 }
 
-const char *get_target_oformat_name(ZigLLVM_ObjectFormatType oformat) {
+const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat) {
     switch (oformat) {
         case ZigLLVM_UnknownObjectFormat: return "unknown";
         case ZigLLVM_COFF: return "coff";
@@ -233,22 +224,25 @@ size_t target_arch_count(void) {
     return array_length(arch_list);
 }
 
-const ArchType *get_target_arch(size_t index) {
-    return &arch_list[index];
+ZigLLVM_ArchType target_arch_enum(size_t index) {
+    assert(index < array_length(arch_list));
+    return arch_list[index];
 }
 
 size_t target_vendor_count(void) {
     return array_length(vendor_list);
 }
 
-ZigLLVM_VendorType get_target_vendor(size_t index) {
+ZigLLVM_VendorType target_vendor_enum(size_t index) {
+    assert(index < array_length(vendor_list));
     return vendor_list[index];
 }
 
 size_t target_os_count(void) {
     return array_length(os_list);
 }
-Os get_target_os(size_t index) {
+Os target_os_enum(size_t index) {
+    assert(index < array_length(os_list));
     return os_list[index];
 }
 
@@ -391,7 +385,7 @@ static Os get_zig_os_type(ZigLLVM_OSType os_type) {
     zig_unreachable();
 }
 
-const char *get_target_os_name(Os os_type) {
+const char *target_os_name(Os os_type) {
     switch (os_type) {
         case OsFreestanding:
             return "freestanding";
@@ -434,80 +428,251 @@ const char *get_target_os_name(Os os_type) {
     zig_unreachable();
 }
 
-size_t target_environ_count(void) {
-    return array_length(environ_list);
+size_t target_abi_count(void) {
+    return array_length(abi_list);
+}
+ZigLLVM_EnvironmentType target_abi_enum(size_t index) {
+    assert(index < array_length(abi_list));
+    return abi_list[index];
 }
-ZigLLVM_EnvironmentType get_target_environ(size_t index) {
-    return environ_list[index];
+const char *target_abi_name(ZigLLVM_EnvironmentType abi) {
+    if (abi == ZigLLVM_UnknownEnvironment)
+        return "none";
+    return ZigLLVMGetEnvironmentTypeName(abi);
 }
 
 void get_native_target(ZigTarget *target) {
     ZigLLVM_OSType os_type;
     ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os
     ZigLLVMGetNativeTarget(
-            &target->arch.arch,
-            &target->arch.sub_arch,
+            &target->arch,
+            &target->sub_arch,
             &target->vendor,
             &os_type,
-            &target->env_type,
+            &target->abi,
             &oformat);
     target->os = get_zig_os_type(os_type);
     target->is_native = true;
+    if (target->abi == ZigLLVM_UnknownEnvironment) {
+        target->abi = target_default_abi(target->arch, target->os);
+    }
 }
 
-void get_unknown_target(ZigTarget *target) {
-    target->arch.arch = ZigLLVM_UnknownArch;
-    target->arch.sub_arch = ZigLLVM_NoSubArch;
-    target->vendor = ZigLLVM_UnknownVendor;
-    target->os = OsFreestanding;
-    target->env_type = ZigLLVM_UnknownEnvironment;
-    target->is_native = false;
+Error target_parse_archsub(ZigLLVM_ArchType *out_arch, ZigLLVM_SubArchType *out_sub,
+        const char *archsub_ptr, size_t archsub_len)
+{
+    for (size_t arch_i = 0; arch_i < array_length(arch_list); arch_i += 1) {
+        ZigLLVM_ArchType arch = arch_list[arch_i];
+        SubArchList sub_arch_list = target_subarch_list(arch);
+        size_t subarch_count = target_subarch_count(sub_arch_list);
+        if (subarch_count == 0) {
+            if (mem_eql_str(archsub_ptr, archsub_len, target_arch_name(arch))) {
+                *out_arch = arch;
+                *out_sub = ZigLLVM_NoSubArch;
+                return ErrorNone;
+            }
+            continue;
+        }
+        for (size_t sub_i = 0; sub_i < subarch_count; sub_i += 1) {
+            ZigLLVM_SubArchType sub = target_subarch_enum(sub_arch_list, sub_i);
+            char arch_name[64];
+            int n = sprintf(arch_name, "%s%s", target_arch_name(arch), target_subarch_name(sub));
+            if (mem_eql_mem(arch_name, n, archsub_ptr, archsub_len)) {
+                *out_arch = arch;
+                *out_sub = sub;
+                return ErrorNone;
+            }
+        }
+    }
+    return ErrorUnknownArchitecture;
 }
 
-static void get_arch_name_raw(char *out_str, ZigLLVM_ArchType arch, ZigLLVM_SubArchType sub_arch) {
-    const char *sub_str = (sub_arch == ZigLLVM_NoSubArch) ? "" : ZigLLVMGetSubArchTypeName(sub_arch);
-    sprintf(out_str, "%s%s", ZigLLVMGetArchTypeName(arch), sub_str);
-}
+SubArchList target_subarch_list(ZigLLVM_ArchType arch) {
+    switch (arch) {
+        case ZigLLVM_UnknownArch:
+            zig_unreachable();
+        case ZigLLVM_arm:
+        case ZigLLVM_armeb:
+        case ZigLLVM_thumb:
+        case ZigLLVM_thumbeb:
+            return SubArchListArm32;
+
+        case ZigLLVM_aarch64:
+        case ZigLLVM_aarch64_be:
+            return SubArchListArm64;
+
+        case ZigLLVM_kalimba:
+            return SubArchListKalimba;
 
-void get_arch_name(char *out_str, const ArchType *arch) {
-    return get_arch_name_raw(out_str, arch->arch, arch->sub_arch);
+        case ZigLLVM_arc:
+        case ZigLLVM_avr:
+        case ZigLLVM_bpfel:
+        case ZigLLVM_bpfeb:
+        case ZigLLVM_hexagon:
+        case ZigLLVM_mips:
+        case ZigLLVM_mipsel:
+        case ZigLLVM_mips64:
+        case ZigLLVM_mips64el:
+        case ZigLLVM_msp430:
+        case ZigLLVM_nios2:
+        case ZigLLVM_ppc:
+        case ZigLLVM_ppc64:
+        case ZigLLVM_ppc64le:
+        case ZigLLVM_r600:
+        case ZigLLVM_amdgcn:
+        case ZigLLVM_riscv32:
+        case ZigLLVM_riscv64:
+        case ZigLLVM_sparc:
+        case ZigLLVM_sparcv9:
+        case ZigLLVM_sparcel:
+        case ZigLLVM_systemz:
+        case ZigLLVM_tce:
+        case ZigLLVM_tcele:
+        case ZigLLVM_x86:
+        case ZigLLVM_x86_64:
+        case ZigLLVM_xcore:
+        case ZigLLVM_nvptx:
+        case ZigLLVM_nvptx64:
+        case ZigLLVM_le32:
+        case ZigLLVM_le64:
+        case ZigLLVM_amdil:
+        case ZigLLVM_amdil64:
+        case ZigLLVM_hsail:
+        case ZigLLVM_hsail64:
+        case ZigLLVM_spir:
+        case ZigLLVM_spir64:
+        case ZigLLVM_shave:
+        case ZigLLVM_lanai:
+        case ZigLLVM_wasm32:
+        case ZigLLVM_wasm64:
+        case ZigLLVM_renderscript32:
+        case ZigLLVM_renderscript64:
+            return SubArchListNone;
+    }
+    zig_unreachable();
 }
 
-int parse_target_arch(const char *str, ArchType *out_arch) {
-    for (size_t i = 0; i < array_length(arch_list); i += 1) {
-        const ArchType *arch = &arch_list[i];
-        char arch_name[50];
-        get_arch_name_raw(arch_name, arch->arch, arch->sub_arch);
-        if (strcmp(arch_name, str) == 0) {
-            *out_arch = *arch;
+size_t target_subarch_count(SubArchList sub_arch_list) {
+    switch (sub_arch_list) {
+        case SubArchListNone:
             return 0;
-        }
+        case SubArchListArm32:
+            return array_length(subarch_list_arm32);
+        case SubArchListArm64:
+            return array_length(subarch_list_arm64);
+        case SubArchListKalimba:
+            return array_length(subarch_list_kalimba);
+    }
+    zig_unreachable();
+}
+
+ZigLLVM_SubArchType target_subarch_enum(SubArchList sub_arch_list, size_t i) {
+    switch (sub_arch_list) {
+        case SubArchListNone:
+            zig_unreachable();
+        case SubArchListArm32:
+            assert(i < array_length(subarch_list_arm32));
+            return subarch_list_arm32[i];
+        case SubArchListArm64:
+            assert(i < array_length(subarch_list_arm64));
+            return subarch_list_arm64[i];
+        case SubArchListKalimba:
+            assert(i < array_length(subarch_list_kalimba));
+            return subarch_list_kalimba[i];
+    }
+    zig_unreachable();
+}
+
+const char *target_subarch_name(ZigLLVM_SubArchType subarch) {
+    return ZigLLVMGetSubArchTypeName(subarch);
+}
+
+size_t target_subarch_list_count(void) {
+    return array_length(subarch_list_list);
+}
+
+SubArchList target_subarch_list_enum(size_t index) {
+    assert(index < array_length(subarch_list_list));
+    return subarch_list_list[index];
+}
+
+const char *target_subarch_list_name(SubArchList sub_arch_list) {
+    switch (sub_arch_list) {
+        case SubArchListNone:
+            return "None";
+        case SubArchListArm32:
+            return "Arm32";
+        case SubArchListArm64:
+            return "Arm64";
+        case SubArchListKalimba:
+            return "Kalimba";
     }
-    return ErrorFileNotFound;
+    zig_unreachable();
 }
 
-int parse_target_os(const char *str, Os *out_os) {
+Error target_parse_os(Os *out_os, const char *os_ptr, size_t os_len) {
     for (size_t i = 0; i < array_length(os_list); i += 1) {
         Os os = os_list[i];
-        const char *os_name = get_target_os_name(os);
-        if (strcmp(os_name, str) == 0) {
+        const char *os_name = target_os_name(os);
+        if (mem_eql_str(os_ptr, os_len, os_name)) {
             *out_os = os;
-            return 0;
+            return ErrorNone;
         }
     }
-    return ErrorFileNotFound;
+    return ErrorUnknownOperatingSystem;
 }
 
-int parse_target_environ(const char *str, ZigLLVM_EnvironmentType *out_environ) {
-    for (size_t i = 0; i < array_length(environ_list); i += 1) {
-        ZigLLVM_EnvironmentType env_type = environ_list[i];
-        const char *environ_name = ZigLLVMGetEnvironmentTypeName(env_type);
-        if (strcmp(environ_name, str) == 0) {
-            *out_environ = env_type;
-            return 0;
+Error target_parse_abi(ZigLLVM_EnvironmentType *out_abi, const char *abi_ptr, size_t abi_len) {
+    for (size_t i = 0; i < array_length(abi_list); i += 1) {
+        ZigLLVM_EnvironmentType abi = abi_list[i];
+        const char *abi_name = target_abi_name(abi);
+        if (mem_eql_str(abi_ptr, abi_len, abi_name)) {
+            *out_abi = abi;
+            return ErrorNone;
+        }
+    }
+    return ErrorUnknownABI;
+}
+
+Error target_parse_triple(ZigTarget *target, const char *triple) {
+    Error err;
+    SplitIterator it = memSplit(str(triple), str("-"));
+
+    Optional<Slice<uint8_t>> opt_archsub = SplitIterator_next(&it);
+    Optional<Slice<uint8_t>> opt_os = SplitIterator_next(&it);
+    Optional<Slice<uint8_t>> opt_abi = SplitIterator_next(&it);
+
+    if (!opt_archsub.is_some)
+        return ErrorMissingArchitecture;
+
+    if (!opt_os.is_some)
+        return ErrorMissingOperatingSystem;
+
+    if ((err = target_parse_archsub(&target->arch, &target->sub_arch,
+                    (char*)opt_archsub.value.ptr, opt_archsub.value.len)))
+    {
+        return err;
+    }
+
+    if ((err = target_parse_os(&target->os, (char*)opt_os.value.ptr, opt_os.value.len))) {
+        return err;
+    }
+
+    if (opt_abi.is_some) {
+        if ((err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len))) {
+            return err;
         }
+    } else {
+        target->abi = target_default_abi(target->arch, target->os);
     }
-    return ErrorFileNotFound;
+
+    target->vendor = ZigLLVM_UnknownVendor;
+    target->is_native = false;
+    return ErrorNone;
+}
+
+const char *target_arch_name(ZigLLVM_ArchType arch) {
+    return ZigLLVMGetArchTypeName(arch);
 }
 
 void init_all_targets(void) {
@@ -519,37 +684,21 @@ void init_all_targets(void) {
 }
 
 void get_target_triple(Buf *triple, const ZigTarget *target) {
-    char arch_name[50];
-    get_arch_name(arch_name, &target->arch);
-
     buf_resize(triple, 0);
-
-    // LLVM WebAssembly output support requires the target to be activated at
-    // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly.
-    //
-    // LLVM determines the output format based on the environment suffix,
-    // defaulting to an object based on the architecture. The default format in
-    // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to
-    // explicitly set this ourself in order for it to work.
-    //
-    // This is fixed in LLVM 7 and you will be able to get wasm output by
-    // using the target triple `wasm32-unknown-unknown-unknown`.
-    if (!strncmp(arch_name, "wasm", 4)) {
-        buf_appendf(triple, "%s-%s-%s-wasm", arch_name,
-                ZigLLVMGetVendorTypeName(target->vendor),
-                ZigLLVMGetOSTypeName(get_llvm_os_type(target->os)));
-    } else {
-        buf_appendf(triple, "%s-%s-%s-%s", arch_name,
-                ZigLLVMGetVendorTypeName(target->vendor),
-                ZigLLVMGetOSTypeName(get_llvm_os_type(target->os)),
-                ZigLLVMGetEnvironmentTypeName(target->env_type));
-    }
+    buf_appendf(triple, "%s%s-%s-%s-%s",
+            ZigLLVMGetArchTypeName(target->arch),
+            ZigLLVMGetSubArchTypeName(target->sub_arch),
+            ZigLLVMGetVendorTypeName(target->vendor),
+            ZigLLVMGetOSTypeName(get_llvm_os_type(target->os)),
+            ZigLLVMGetEnvironmentTypeName(target->abi));
 }
 
 bool target_is_darwin(const ZigTarget *target) {
     switch (target->os) {
         case OsMacOSX:
         case OsIOS:
+        case OsWatchOS:
+        case OsTvOS:
             return true;
         default:
             return false;
@@ -562,8 +711,8 @@ ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target) {
     } else if (target_is_darwin(target)) {
         return ZigLLVM_MachO;
     }
-    if (target->arch.arch == ZigLLVM_wasm32 ||
-        target->arch.arch == ZigLLVM_wasm64)
+    if (target->arch == ZigLLVM_wasm32 ||
+        target->arch == ZigLLVM_wasm64)
     {
         return ZigLLVM_Wasm;
     }
@@ -639,7 +788,7 @@ static int get_arch_pointer_bit_width(ZigLLVM_ArchType arch) {
 uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
     switch (target->os) {
         case OsFreestanding:
-            switch (target->arch.arch) {
+            switch (target->arch) {
                 case ZigLLVM_msp430:
                     switch (id) {
                         case CIntTypeShort:
@@ -667,7 +816,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
                             return 32;
                         case CIntTypeLong:
                         case CIntTypeULong:
-                            return get_arch_pointer_bit_width(target->arch.arch);
+                            return get_arch_pointer_bit_width(target->arch);
                         case CIntTypeLongLong:
                         case CIntTypeULongLong:
                             return 64;
@@ -690,7 +839,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
                     return 32;
                 case CIntTypeLong:
                 case CIntTypeULong:
-                    return get_arch_pointer_bit_width(target->arch.arch);
+                    return get_arch_pointer_bit_width(target->arch);
                 case CIntTypeLongLong:
                 case CIntTypeULongLong:
                     return 64;
@@ -748,7 +897,7 @@ bool target_allows_addr_zero(const ZigTarget *target) {
 }
 
 const char *target_o_file_ext(const ZigTarget *target) {
-    if (target->env_type == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) {
+    if (target->abi == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) {
         return ".obj";
     } else {
         return ".o";
@@ -798,7 +947,7 @@ enum FloatAbi {
 };
 
 static FloatAbi get_float_abi(const ZigTarget *target) {
-    const ZigLLVM_EnvironmentType env = target->env_type;
+    const ZigLLVM_EnvironmentType env = target->abi;
     if (env == ZigLLVM_GNUEABIHF ||
         env == ZigLLVM_EABIHF ||
         env == ZigLLVM_MuslEABIHF)
@@ -813,71 +962,154 @@ static bool is_64_bit(ZigLLVM_ArchType arch) {
     return get_arch_pointer_bit_width(arch) == 64;
 }
 
-Buf *target_dynamic_linker(const ZigTarget *target) {
-    if (target->os == OsFreeBSD) {
-        return buf_create_from_str("/libexec/ld-elf.so.1");
-    }
-    if (target->os == OsNetBSD) {
-        return buf_create_from_str("/libexec/ld.elf_so");
-    }
+const char *target_dynamic_linker(const ZigTarget *target) {
+    switch (target->os) {
+        case OsFreeBSD:
+            return "/libexec/ld-elf.so.1";
+        case OsNetBSD:
+            return "/libexec/ld.elf_so";
+        case OsLinux: {
+            const ZigLLVM_EnvironmentType abi = target->abi;
+            if (abi == ZigLLVM_Android) {
+                if (is_64_bit(target->arch)) {
+                    return "/system/bin/linker64";
+                } else {
+                    return "/system/bin/linker";
+                }
+            }
 
-    const ZigLLVM_ArchType arch = target->arch.arch;
-    const ZigLLVM_EnvironmentType env = target->env_type;
+            switch (target->arch) {
+                case ZigLLVM_UnknownArch:
+                    zig_unreachable();
+                case ZigLLVM_x86:
+                case ZigLLVM_sparc:
+                case ZigLLVM_sparcel:
+                    return "/lib/ld-linux.so.2";
+
+                case ZigLLVM_aarch64:
+                    return "/lib/ld-linux-aarch64.so.1";
+
+                case ZigLLVM_aarch64_be:
+                    return "/lib/ld-linux-aarch64_be.so.1";
+
+                case ZigLLVM_arm:
+                case ZigLLVM_thumb:
+                    if (get_float_abi(target) == FloatAbiHard) {
+                        return "/lib/ld-linux-armhf.so.3";
+                    } else {
+                        return "/lib/ld-linux.so.3";
+                    }
 
-    if (env == ZigLLVM_Android) {
-        if (is_64_bit(arch)) {
-            return buf_create_from_str("/system/bin/linker64");
-        } else {
-            return buf_create_from_str("/system/bin/linker");
-        }
-    } else if (arch == ZigLLVM_x86 ||
-            arch == ZigLLVM_sparc ||
-            arch == ZigLLVM_sparcel)
-    {
-        return buf_create_from_str("/lib/ld-linux.so.2");
-    } else if (arch == ZigLLVM_aarch64) {
-        return buf_create_from_str("/lib/ld-linux-aarch64.so.1");
-    } else if (arch == ZigLLVM_aarch64_be) {
-        return buf_create_from_str("/lib/ld-linux-aarch64_be.so.1");
-    } else if (arch == ZigLLVM_arm || arch == ZigLLVM_thumb) {
-        if (get_float_abi(target) == FloatAbiHard) {
-            return buf_create_from_str("/lib/ld-linux-armhf.so.3");
-        } else {
-            return buf_create_from_str("/lib/ld-linux.so.3");
-        }
-    } else if (arch == ZigLLVM_armeb || arch == ZigLLVM_thumbeb) {
-        if (get_float_abi(target) == FloatAbiHard) {
-            return buf_create_from_str("/lib/ld-linux-armhf.so.3");
-        } else {
-            return buf_create_from_str("/lib/ld-linux.so.3");
+                case ZigLLVM_armeb:
+                case ZigLLVM_thumbeb:
+                    if (get_float_abi(target) == FloatAbiHard) {
+                        return "/lib/ld-linux-armhf.so.3";
+                    } else {
+                        return "/lib/ld-linux.so.3";
+                    }
+
+                case ZigLLVM_mips:
+                case ZigLLVM_mipsel:
+                case ZigLLVM_mips64:
+                case ZigLLVM_mips64el:
+                    zig_panic("TODO implement target_dynamic_linker for mips");
+
+                case ZigLLVM_ppc:
+                    return "/lib/ld.so.1";
+
+                case ZigLLVM_ppc64:
+                    return "/lib64/ld64.so.2";
+
+                case ZigLLVM_ppc64le:
+                    return "/lib64/ld64.so.2";
+
+                case ZigLLVM_systemz:
+                    return "/lib64/ld64.so.1";
+
+                case ZigLLVM_sparcv9:
+                    return "/lib64/ld-linux.so.2";
+
+                case ZigLLVM_x86_64:
+                    if (abi == ZigLLVM_GNUX32) {
+                        return "/libx32/ld-linux-x32.so.2";
+                    }
+                    if (abi == ZigLLVM_Musl || abi == ZigLLVM_MuslEABI || abi == ZigLLVM_MuslEABIHF) {
+                        return "/lib/ld-musl-x86_64.so.1";
+                    }
+                    return "/lib64/ld-linux-x86-64.so.2";
+
+                case ZigLLVM_wasm32:
+                case ZigLLVM_wasm64:
+                    return nullptr;
+
+                case ZigLLVM_arc:
+                case ZigLLVM_avr:
+                case ZigLLVM_bpfel:
+                case ZigLLVM_bpfeb:
+                case ZigLLVM_hexagon:
+                case ZigLLVM_msp430:
+                case ZigLLVM_nios2:
+                case ZigLLVM_r600:
+                case ZigLLVM_amdgcn:
+                case ZigLLVM_riscv32:
+                case ZigLLVM_riscv64:
+                case ZigLLVM_tce:
+                case ZigLLVM_tcele:
+                case ZigLLVM_xcore:
+                case ZigLLVM_nvptx:
+                case ZigLLVM_nvptx64:
+                case ZigLLVM_le32:
+                case ZigLLVM_le64:
+                case ZigLLVM_amdil:
+                case ZigLLVM_amdil64:
+                case ZigLLVM_hsail:
+                case ZigLLVM_hsail64:
+                case ZigLLVM_spir:
+                case ZigLLVM_spir64:
+                case ZigLLVM_kalimba:
+                case ZigLLVM_shave:
+                case ZigLLVM_lanai:
+                case ZigLLVM_renderscript32:
+                case ZigLLVM_renderscript64:
+                    zig_panic("TODO implement target_dynamic_linker for this arch");
+            }
+            zig_unreachable();
         }
-    } else if (arch == ZigLLVM_mips || arch == ZigLLVM_mipsel ||
-            arch == ZigLLVM_mips64 || arch == ZigLLVM_mips64el)
-    {
-        // when you want to solve this TODO, grep clang codebase for
-        // getLinuxDynamicLinker
-        zig_panic("TODO figure out MIPS dynamic linker name");
-    } else if (arch == ZigLLVM_ppc) {
-        return buf_create_from_str("/lib/ld.so.1");
-    } else if (arch == ZigLLVM_ppc64) {
-        return buf_create_from_str("/lib64/ld64.so.2");
-    } else if (arch == ZigLLVM_ppc64le) {
-        return buf_create_from_str("/lib64/ld64.so.2");
-    } else if (arch == ZigLLVM_systemz) {
-        return buf_create_from_str("/lib64/ld64.so.1");
-    } else if (arch == ZigLLVM_sparcv9) {
-        return buf_create_from_str("/lib64/ld-linux.so.2");
-    } else if (arch == ZigLLVM_x86_64 &&
-            env == ZigLLVM_GNUX32)
-    {
-        return buf_create_from_str("/libx32/ld-linux-x32.so.2");
-    } else if (arch == ZigLLVM_x86_64 &&
-            (env == ZigLLVM_Musl || env == ZigLLVM_MuslEABI || env == ZigLLVM_MuslEABIHF))
-    {
-        return buf_create_from_str("/lib/ld-musl-x86_64.so.1");
-    } else {
-        return buf_create_from_str("/lib64/ld-linux-x86-64.so.2");
+        case OsFreestanding:
+        case OsIOS:
+        case OsTvOS:
+        case OsWatchOS:
+        case OsMacOSX:
+            return nullptr;
+
+        case OsAnanas:
+        case OsCloudABI:
+        case OsDragonFly:
+        case OsFuchsia:
+        case OsKFreeBSD:
+        case OsLv2:
+        case OsOpenBSD:
+        case OsSolaris:
+        case OsWindows:
+        case OsHaiku:
+        case OsMinix:
+        case OsRTEMS:
+        case OsNaCl:
+        case OsCNK:
+        case OsAIX:
+        case OsCUDA:
+        case OsNVCL:
+        case OsAMDHSA:
+        case OsPS4:
+        case OsELFIAMCU:
+        case OsMesa3D:
+        case OsContiki:
+        case OsAMDPAL:
+        case OsZen:
+        case OsUefi:
+            zig_panic("TODO implement target_dynamic_linker for this OS");
     }
+    zig_unreachable();
 }
 
 bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target) {
@@ -888,15 +1120,15 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target
         return true;
     }
 
-    if (guest_target->os == host_target->os && guest_target->arch.arch == host_target->arch.arch &&
-        guest_target->arch.sub_arch == host_target->arch.sub_arch)
+    if (guest_target->os == host_target->os && guest_target->arch == host_target->arch &&
+        guest_target->sub_arch == host_target->sub_arch)
     {
         // OS, arch, and sub-arch match
         return true;
     }
 
     if (guest_target->os == OsWindows && host_target->os == OsWindows &&
-        host_target->arch.arch == ZigLLVM_x86_64 && guest_target->arch.arch == ZigLLVM_x86)
+        host_target->arch == ZigLLVM_x86_64 && guest_target->arch == ZigLLVM_x86)
     {
         // 64-bit windows can run 32-bit programs
         return true;
@@ -905,8 +1137,8 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target
     return false;
 }
 
-const char *arch_stack_pointer_register_name(const ArchType *arch) {
-    switch (arch->arch) {
+const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {
+    switch (arch) {
         case ZigLLVM_UnknownArch:
             zig_unreachable();
         case ZigLLVM_x86:
@@ -969,7 +1201,7 @@ const char *arch_stack_pointer_register_name(const ArchType *arch) {
 }
 
 bool target_is_arm(const ZigTarget *target) {
-    switch (target->arch.arch) {
+    switch (target->arch) {
         case ZigLLVM_UnknownArch:
             zig_unreachable();
         case ZigLLVM_aarch64:
@@ -1031,12 +1263,12 @@ bool target_is_arm(const ZigTarget *target) {
 
 // Valgrind supports more, but Zig does not support them yet.
 bool target_has_valgrind_support(const ZigTarget *target) {
-    switch (target->arch.arch) {
+    switch (target->arch) {
         case ZigLLVM_UnknownArch:
             zig_unreachable();
         case ZigLLVM_x86_64:
             return (target->os == OsLinux || target_is_darwin(target) || target->os == OsSolaris ||
-                (target->os == OsWindows && target->env_type != ZigLLVM_MSVC));
+                (target->os == OsWindows && target->abi != ZigLLVM_MSVC));
         default:
             return false;
     }
@@ -1055,3 +1287,46 @@ bool target_supports_fpic(const ZigTarget *target) {
   // C compiler argument is valid.
   return target->os != OsWindows;
 }
+
+ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) {
+    switch (os) {
+        case OsFreestanding:
+        case OsAnanas:
+        case OsCloudABI:
+        case OsDragonFly:
+        case OsLv2:
+        case OsSolaris:
+        case OsHaiku:
+        case OsMinix:
+        case OsRTEMS:
+        case OsNaCl:
+        case OsCNK:
+        case OsAIX:
+        case OsCUDA:
+        case OsNVCL:
+        case OsAMDHSA:
+        case OsPS4:
+        case OsELFIAMCU:
+        case OsMesa3D:
+        case OsContiki:
+        case OsAMDPAL:
+        case OsZen:
+            return ZigLLVM_EABI;
+        case OsOpenBSD:
+        case OsMacOSX:
+        case OsFreeBSD:
+        case OsIOS:
+        case OsTvOS:
+        case OsWatchOS:
+        case OsFuchsia:
+        case OsKFreeBSD:
+        case OsNetBSD:
+            return ZigLLVM_GNU;
+        case OsWindows:
+        case OsUefi:
+            return ZigLLVM_MSVC;
+        case OsLinux:
+            return ZigLLVM_Musl;
+    }
+    zig_unreachable();
+}
src/target.hpp
@@ -12,11 +12,6 @@
 
 struct Buf;
 
-struct ArchType {
-    ZigLLVM_ArchType arch;
-    ZigLLVM_SubArchType sub_arch;
-};
-
 // Synchronize with target.cpp::os_list
 enum Os {
     OsFreestanding,
@@ -54,6 +49,14 @@ enum Os {
     OsUefi,
 };
 
+// Synchronize with target.cpp::subarch_list_list
+enum SubArchList {
+    SubArchListNone,
+    SubArchListArm32,
+    SubArchListArm64,
+    SubArchListKalimba,
+};
+
 enum TargetSubsystem {
     TargetSubsystemAuto, // Zig should infer the subsystem
     TargetSubsystemConsole,
@@ -67,10 +70,11 @@ enum TargetSubsystem {
 };
 
 struct ZigTarget {
-    ArchType arch;
+    ZigLLVM_ArchType arch;
+    ZigLLVM_SubArchType sub_arch;
     ZigLLVM_VendorType vendor;
     Os os;
-    ZigLLVM_EnvironmentType env_type;
+    ZigLLVM_EnvironmentType abi;
     bool is_native;
 };
 
@@ -87,39 +91,50 @@ enum CIntType {
     CIntTypeCount,
 };
 
+Error target_parse_triple(ZigTarget *target, const char *triple);
+Error target_parse_archsub(ZigLLVM_ArchType *arch, ZigLLVM_SubArchType *sub,
+        const char *archsub_ptr, size_t archsub_len);
+Error target_parse_os(Os *os, const char *os_ptr, size_t os_len);
+Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len);
+
 size_t target_arch_count(void);
-const ArchType *get_target_arch(size_t index);
-void get_arch_name(char *out_str, const ArchType *arch);
+ZigLLVM_ArchType target_arch_enum(size_t index);
+const char *target_arch_name(ZigLLVM_ArchType arch);
+
+SubArchList target_subarch_list(ZigLLVM_ArchType arch);
+size_t target_subarch_count(SubArchList sub_arch_list);
+ZigLLVM_SubArchType target_subarch_enum(SubArchList subarch_list, size_t index);
+const char *target_subarch_name(ZigLLVM_SubArchType subarch);
 
-const char *arch_stack_pointer_register_name(const ArchType *arch);
+size_t target_subarch_list_count(void);
+SubArchList target_subarch_list_enum(size_t index);
+const char *target_subarch_list_name(SubArchList sub_arch_list);
+
+const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch);
 
 size_t target_vendor_count(void);
-ZigLLVM_VendorType get_target_vendor(size_t index);
+ZigLLVM_VendorType target_vendor_enum(size_t index);
 
 size_t target_os_count(void);
-Os get_target_os(size_t index);
-const char *get_target_os_name(Os os_type);
+Os target_os_enum(size_t index);
+const char *target_os_name(Os os_type);
 
-size_t target_environ_count(void);
-ZigLLVM_EnvironmentType get_target_environ(size_t index);
+size_t target_abi_count(void);
+ZigLLVM_EnvironmentType target_abi_enum(size_t index);
+const char *target_abi_name(ZigLLVM_EnvironmentType abi);
+ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os);
 
 
 size_t target_oformat_count(void);
-ZigLLVM_ObjectFormatType get_target_oformat(size_t index);
-const char *get_target_oformat_name(ZigLLVM_ObjectFormatType oformat);
+ZigLLVM_ObjectFormatType target_oformat_enum(size_t index);
+const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat);
 ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target);
 
 void get_native_target(ZigTarget *target);
-void get_unknown_target(ZigTarget *target);
-
-int parse_target_arch(const char *str, ArchType *arch);
-int parse_target_os(const char *str, Os *os);
-int parse_target_environ(const char *str, ZigLLVM_EnvironmentType *env_type);
+void get_target_triple(Buf *triple, const ZigTarget *target);
 
 void init_all_targets(void);
 
-void get_target_triple(Buf *triple, const ZigTarget *target);
-
 void resolve_target_object_format(ZigTarget *target);
 
 uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id);
@@ -131,7 +146,7 @@ const char *target_exe_file_ext(const ZigTarget *target);
 const char *target_lib_file_ext(const ZigTarget *target, bool is_static,
         size_t version_major, size_t version_minor, size_t version_patch);
 
-Buf *target_dynamic_linker(const ZigTarget *target);
+const char *target_dynamic_linker(const ZigTarget *target);
 
 bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target);
 ZigLLVM_OSType get_llvm_os_type(Os os_type);
src/util.hpp
@@ -147,11 +147,14 @@ static inline T clamp(T min_value, T value, T max_value) {
     return max(min(value, max_value), min_value);
 }
 
-static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) {
-    size_t str_len = strlen(str);
-    if (str_len != mem_len)
+static inline bool mem_eql_mem(const char *a_ptr, size_t a_len, const char *b_ptr, size_t b_len) {
+    if (a_len != b_len)
         return false;
-    return memcmp(mem, str, mem_len) == 0;
+    return memcmp(a_ptr, b_ptr, a_len) == 0;
+}
+
+static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) {
+    return mem_eql_mem(mem, mem_len, str, strlen(str));
 }
 
 static inline bool is_power_of_2(uint64_t x) {
src/zig_llvm.cpp
@@ -730,7 +730,7 @@ void ZigLLVMGetNativeTarget(ZigLLVM_ArchType *arch_type, ZigLLVM_SubArchType *su
 const char *ZigLLVMGetSubArchTypeName(ZigLLVM_SubArchType sub_arch) {
     switch (sub_arch) {
         case ZigLLVM_NoSubArch:
-            return "(none)";
+            return "";
         case ZigLLVM_ARMSubArch_v8_4a:
             return "v8_4a";
         case ZigLLVM_ARMSubArch_v8_3a:
src/zig_llvm.h
@@ -215,7 +215,7 @@ ZIG_EXTERN_C void ZigLLVMParseCommandLineOptions(size_t argc, const char *const
 
 
 // copied from include/llvm/ADT/Triple.h
-
+// synchronize with target.cpp::arch_list
 enum ZigLLVM_ArchType {
     ZigLLVM_UnknownArch,
 
@@ -273,6 +273,7 @@ enum ZigLLVM_ArchType {
     ZigLLVM_LastArchType = ZigLLVM_renderscript64
 };
 
+// synchronize with lists in target.cpp
 enum ZigLLVM_SubArchType {
     ZigLLVM_NoSubArch,
 
@@ -404,7 +405,7 @@ ZIG_EXTERN_C const char *ZigLLVMGetArchTypeName(enum ZigLLVM_ArchType arch);
 ZIG_EXTERN_C const char *ZigLLVMGetSubArchTypeName(enum ZigLLVM_SubArchType sub_arch);
 ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor);
 ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os);
-ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType env_type);
+ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi);
 
 ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,
         void (*append_diagnostic)(void *, const char *, size_t), void *context);
src-self-hosted/link.zig
@@ -326,7 +326,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
     switch (ctx.comp.target.getArch()) {
         builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"),
         builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"),
-        builtin.Arch.aarch64v8 => try ctx.args.append(c"-MACHINE:ARM"),
+        builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"),
         else => return error.UnsupportedLinkArchitecture,
     }
 
@@ -552,7 +552,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
                 }
             },
             DarwinPlatform.Kind.IPhoneOS => {
-                if (ctx.comp.target.getArch() == builtin.Arch.aarch64v8) {
+                if (ctx.comp.target.getArch() == builtin.Arch.aarch64) {
                     // iOS does not need any crt1 files for arm64
                 } else if (platform.versionLessThan(3, 1)) {
                     try ctx.args.append(c"-lcrt1.o");
src-self-hosted/main.zig
@@ -154,9 +154,7 @@ const usage_build_generic =
     \\    release-small              optimize for small binary, safety off
     \\  --static                     Output will be statically linked
     \\  --strip                      Exclude debug symbols
-    \\  --target-arch [name]         Specify target architecture
-    \\  --target-environ [name]      Specify target environment
-    \\  --target-os [name]           Specify target operating system
+    \\  -target [name]               <arch><sub>-<os>-<abi> see the targets command
     \\  --verbose-tokenize           Turn on compiler debug output for tokenization
     \\  --verbose-ast-tree           Turn on compiler debug output for parsing into an AST (tree view)
     \\  --verbose-ast-fmt            Turn on compiler debug output for parsing into an AST (render source)
@@ -220,9 +218,7 @@ const args_build_generic = []Flag{
     Flag.Bool("--pkg-end"),
     Flag.Bool("--static"),
     Flag.Bool("--strip"),
-    Flag.Arg1("--target-arch"),
-    Flag.Arg1("--target-environ"),
-    Flag.Arg1("--target-os"),
+    Flag.Arg1("-target"),
     Flag.Bool("--verbose-tokenize"),
     Flag.Bool("--verbose-ast-tree"),
     Flag.Bool("--verbose-ast-fmt"),
@@ -839,15 +835,15 @@ fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void {
     }
     try stdout.write("\n");
 
-    try stdout.write("Environments:\n");
+    try stdout.write("C ABIs:\n");
     {
         comptime var i: usize = 0;
-        inline while (i < @memberCount(builtin.Environ)) : (i += 1) {
-            comptime const environ_tag = @memberName(builtin.Environ, i);
+        inline while (i < @memberCount(builtin.Abi)) : (i += 1) {
+            comptime const abi_tag = @memberName(builtin.Abi, i);
             // NOTE: Cannot use empty string, see #918.
-            comptime const native_str = if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n";
+            comptime const native_str = if (comptime mem.eql(u8, abi_tag, @tagName(builtin.abi))) " (native)\n" else "\n";
 
-            try stdout.print("  {}{}", environ_tag, native_str);
+            try stdout.print("  {}{}", abi_tag, native_str);
         }
     }
 }
src-self-hosted/target.zig
@@ -16,7 +16,7 @@ pub const Target = union(enum) {
     pub const Cross = struct {
         arch: builtin.Arch,
         os: builtin.Os,
-        environ: builtin.Environ,
+        abi: builtin.Abi,
         object_format: builtin.ObjectFormat,
     };
 
@@ -49,16 +49,16 @@ pub const Target = union(enum) {
     }
 
     pub fn getArch(self: Target) builtin.Arch {
-        return switch (self) {
-            Target.Native => builtin.arch,
-            @TagType(Target).Cross => |t| t.arch,
-        };
+        switch (self) {
+            Target.Native => return builtin.arch,
+            @TagType(Target).Cross => |t| return t.arch,
+        }
     }
 
-    pub fn getEnviron(self: Target) builtin.Environ {
+    pub fn getAbi(self: Target) builtin.Abi {
         return switch (self) {
-            Target.Native => builtin.environ,
-            @TagType(Target).Cross => |t| t.environ,
+            Target.Native => builtin.abi,
+            @TagType(Target).Cross => |t| t.abi,
         };
     }
 
@@ -93,46 +93,10 @@ pub const Target = union(enum) {
     /// TODO expose the arch and subarch separately
     pub fn isArmOrThumb(self: Target) bool {
         return switch (self.getArch()) {
-            builtin.Arch.armv8_3a,
-            builtin.Arch.armv8_2a,
-            builtin.Arch.armv8_1a,
-            builtin.Arch.armv8,
-            builtin.Arch.armv8r,
-            builtin.Arch.armv8m_baseline,
-            builtin.Arch.armv8m_mainline,
-            builtin.Arch.armv7,
-            builtin.Arch.armv7em,
-            builtin.Arch.armv7m,
-            builtin.Arch.armv7s,
-            builtin.Arch.armv7k,
-            builtin.Arch.armv7ve,
-            builtin.Arch.armv6,
-            builtin.Arch.armv6m,
-            builtin.Arch.armv6k,
-            builtin.Arch.armv6t2,
-            builtin.Arch.armv5,
-            builtin.Arch.armv5te,
-            builtin.Arch.armv4t,
-            builtin.Arch.armebv8_3a,
-            builtin.Arch.armebv8_2a,
-            builtin.Arch.armebv8_1a,
-            builtin.Arch.armebv8,
-            builtin.Arch.armebv8r,
-            builtin.Arch.armebv8m_baseline,
-            builtin.Arch.armebv8m_mainline,
-            builtin.Arch.armebv7,
-            builtin.Arch.armebv7em,
-            builtin.Arch.armebv7m,
-            builtin.Arch.armebv7s,
-            builtin.Arch.armebv7k,
-            builtin.Arch.armebv7ve,
-            builtin.Arch.armebv6,
-            builtin.Arch.armebv6m,
-            builtin.Arch.armebv6k,
-            builtin.Arch.armebv6t2,
-            builtin.Arch.armebv5,
-            builtin.Arch.armebv5te,
-            builtin.Arch.armebv4t,
+            builtin.Arch.arm,
+            builtin.Arch.armeb,
+            builtin.Arch.aarch64,
+            builtin.Arch.aarch64_be,
             builtin.Arch.thumb,
             builtin.Arch.thumbeb,
             => true,
@@ -155,14 +119,14 @@ pub const Target = union(enum) {
         // LLVM WebAssembly output support requires the target to be activated at
         // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly.
         //
-        // LLVM determines the output format based on the environment suffix,
+        // LLVM determines the output format based on the abi suffix,
         // defaulting to an object based on the architecture. The default format in
         // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to
         // explicitly set this ourself in order for it to work.
         //
         // This is fixed in LLVM 7 and you will be able to get wasm output by
         // using the target triple `wasm32-unknown-unknown-unknown`.
-        const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron());
+        const env_name = if (self.isWasm()) "wasm" else @tagName(self.getAbi());
 
         var out = &std.io.BufferOutStream.init(&result).stream;
         try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name);
@@ -181,46 +145,8 @@ pub const Target = union(enum) {
             => return 16,
 
             builtin.Arch.arc,
-            builtin.Arch.armv8_3a,
-            builtin.Arch.armv8_2a,
-            builtin.Arch.armv8_1a,
-            builtin.Arch.armv8,
-            builtin.Arch.armv8r,
-            builtin.Arch.armv8m_baseline,
-            builtin.Arch.armv8m_mainline,
-            builtin.Arch.armv7,
-            builtin.Arch.armv7em,
-            builtin.Arch.armv7m,
-            builtin.Arch.armv7s,
-            builtin.Arch.armv7k,
-            builtin.Arch.armv7ve,
-            builtin.Arch.armv6,
-            builtin.Arch.armv6m,
-            builtin.Arch.armv6k,
-            builtin.Arch.armv6t2,
-            builtin.Arch.armv5,
-            builtin.Arch.armv5te,
-            builtin.Arch.armv4t,
-            builtin.Arch.armebv8_3a,
-            builtin.Arch.armebv8_2a,
-            builtin.Arch.armebv8_1a,
-            builtin.Arch.armebv8,
-            builtin.Arch.armebv8r,
-            builtin.Arch.armebv8m_baseline,
-            builtin.Arch.armebv8m_mainline,
-            builtin.Arch.armebv7,
-            builtin.Arch.armebv7em,
-            builtin.Arch.armebv7m,
-            builtin.Arch.armebv7s,
-            builtin.Arch.armebv7k,
-            builtin.Arch.armebv7ve,
-            builtin.Arch.armebv6,
-            builtin.Arch.armebv6m,
-            builtin.Arch.armebv6k,
-            builtin.Arch.armebv6t2,
-            builtin.Arch.armebv5,
-            builtin.Arch.armebv5te,
-            builtin.Arch.armebv4t,
+            builtin.Arch.arm,
+            builtin.Arch.armeb,
             builtin.Arch.hexagon,
             builtin.Arch.le32,
             builtin.Arch.mips,
@@ -241,29 +167,15 @@ pub const Target = union(enum) {
             builtin.Arch.amdil,
             builtin.Arch.hsail,
             builtin.Arch.spir,
-            builtin.Arch.kalimbav3,
-            builtin.Arch.kalimbav4,
-            builtin.Arch.kalimbav5,
+            builtin.Arch.kalimba,
             builtin.Arch.shave,
             builtin.Arch.lanai,
             builtin.Arch.wasm32,
             builtin.Arch.renderscript32,
             => return 32,
 
-            builtin.Arch.aarch64v8_3a,
-            builtin.Arch.aarch64v8_2a,
-            builtin.Arch.aarch64v8_1a,
-            builtin.Arch.aarch64v8,
-            builtin.Arch.aarch64v8r,
-            builtin.Arch.aarch64v8m_baseline,
-            builtin.Arch.aarch64v8m_mainline,
-            builtin.Arch.aarch64_bev8_3a,
-            builtin.Arch.aarch64_bev8_2a,
-            builtin.Arch.aarch64_bev8_1a,
-            builtin.Arch.aarch64_bev8,
-            builtin.Arch.aarch64_bev8r,
-            builtin.Arch.aarch64_bev8m_baseline,
-            builtin.Arch.aarch64_bev8m_mainline,
+            builtin.Arch.aarch64,
+            builtin.Arch.aarch64_be,
             builtin.Arch.mips64,
             builtin.Arch.mips64el,
             builtin.Arch.powerpc64,
@@ -287,17 +199,17 @@ pub const Target = union(enum) {
     }
 
     pub fn getFloatAbi(self: Target) FloatAbi {
-        return switch (self.getEnviron()) {
-            builtin.Environ.gnueabihf,
-            builtin.Environ.eabihf,
-            builtin.Environ.musleabihf,
+        return switch (self.getAbi()) {
+            builtin.Abi.gnueabihf,
+            builtin.Abi.eabihf,
+            builtin.Abi.musleabihf,
             => FloatAbi.Hard,
             else => FloatAbi.Soft,
         };
     }
 
     pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
-        const env = self.getEnviron();
+        const env = self.getAbi();
         const arch = self.getArch();
         const os = self.getOs();
         switch (os) {
@@ -306,21 +218,21 @@ pub const Target = union(enum) {
             },
             builtin.Os.linux => {
                 switch (env) {
-                    builtin.Environ.android => {
+                    builtin.Abi.android => {
                         if (self.is64bit()) {
                             return "/system/bin/linker64";
                         } else {
                             return "/system/bin/linker";
                         }
                     },
-                    builtin.Environ.gnux32 => {
+                    builtin.Abi.gnux32 => {
                         if (arch == builtin.Arch.x86_64) {
                             return "/libx32/ld-linux-x32.so.2";
                         }
                     },
-                    builtin.Environ.musl,
-                    builtin.Environ.musleabi,
-                    builtin.Environ.musleabihf,
+                    builtin.Abi.musl,
+                    builtin.Abi.musleabi,
+                    builtin.Abi.musleabihf,
                     => {
                         if (arch == builtin.Arch.x86_64) {
                             return "/lib/ld-musl-x86_64.so.1";
@@ -334,70 +246,18 @@ pub const Target = union(enum) {
                     builtin.Arch.sparcel,
                     => return "/lib/ld-linux.so.2",
 
-                    builtin.Arch.aarch64v8_3a,
-                    builtin.Arch.aarch64v8_2a,
-                    builtin.Arch.aarch64v8_1a,
-                    builtin.Arch.aarch64v8,
-                    builtin.Arch.aarch64v8r,
-                    builtin.Arch.aarch64v8m_baseline,
-                    builtin.Arch.aarch64v8m_mainline,
-                    => return "/lib/ld-linux-aarch64.so.1",
-
-                    builtin.Arch.aarch64_bev8_3a,
-                    builtin.Arch.aarch64_bev8_2a,
-                    builtin.Arch.aarch64_bev8_1a,
-                    builtin.Arch.aarch64_bev8,
-                    builtin.Arch.aarch64_bev8r,
-                    builtin.Arch.aarch64_bev8m_baseline,
-                    builtin.Arch.aarch64_bev8m_mainline,
-                    => return "/lib/ld-linux-aarch64_be.so.1",
-
-                    builtin.Arch.armv8_3a,
-                    builtin.Arch.armv8_2a,
-                    builtin.Arch.armv8_1a,
-                    builtin.Arch.armv8,
-                    builtin.Arch.armv8r,
-                    builtin.Arch.armv8m_baseline,
-                    builtin.Arch.armv8m_mainline,
-                    builtin.Arch.armv7,
-                    builtin.Arch.armv7em,
-                    builtin.Arch.armv7m,
-                    builtin.Arch.armv7s,
-                    builtin.Arch.armv7k,
-                    builtin.Arch.armv7ve,
-                    builtin.Arch.armv6,
-                    builtin.Arch.armv6m,
-                    builtin.Arch.armv6k,
-                    builtin.Arch.armv6t2,
-                    builtin.Arch.armv5,
-                    builtin.Arch.armv5te,
-                    builtin.Arch.armv4t,
+                    builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1",
+
+                    builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1",
+
+                    builtin.Arch.arm,
                     builtin.Arch.thumb,
                     => return switch (self.getFloatAbi()) {
                         FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
                         else => return "/lib/ld-linux.so.3",
                     },
 
-                    builtin.Arch.armebv8_3a,
-                    builtin.Arch.armebv8_2a,
-                    builtin.Arch.armebv8_1a,
-                    builtin.Arch.armebv8,
-                    builtin.Arch.armebv8r,
-                    builtin.Arch.armebv8m_baseline,
-                    builtin.Arch.armebv8m_mainline,
-                    builtin.Arch.armebv7,
-                    builtin.Arch.armebv7em,
-                    builtin.Arch.armebv7m,
-                    builtin.Arch.armebv7s,
-                    builtin.Arch.armebv7k,
-                    builtin.Arch.armebv7ve,
-                    builtin.Arch.armebv6,
-                    builtin.Arch.armebv6m,
-                    builtin.Arch.armebv6k,
-                    builtin.Arch.armebv6t2,
-                    builtin.Arch.armebv5,
-                    builtin.Arch.armebv5te,
-                    builtin.Arch.armebv4t,
+                    builtin.Arch.armeb,
                     builtin.Arch.thumbeb,
                     => return switch (self.getFloatAbi()) {
                         FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
@@ -441,9 +301,7 @@ pub const Target = union(enum) {
                     builtin.Arch.hsail64,
                     builtin.Arch.spir,
                     builtin.Arch.spir64,
-                    builtin.Arch.kalimbav3,
-                    builtin.Arch.kalimbav4,
-                    builtin.Arch.kalimbav5,
+                    builtin.Arch.kalimba,
                     builtin.Arch.shave,
                     builtin.Arch.lanai,
                     builtin.Arch.wasm32,
@@ -566,35 +424,9 @@ pub const Target = union(enum) {
     pub fn getDarwinArchString(self: Target) []const u8 {
         const arch = self.getArch();
         switch (arch) {
-            builtin.Arch.aarch64v8_3a,
-            builtin.Arch.aarch64v8_2a,
-            builtin.Arch.aarch64v8_1a,
-            builtin.Arch.aarch64v8,
-            builtin.Arch.aarch64v8r,
-            builtin.Arch.aarch64v8m_baseline,
-            builtin.Arch.aarch64v8m_mainline,
-            => return "arm64",
+            builtin.Arch.aarch64 => return "arm64",
             builtin.Arch.thumb,
-            builtin.Arch.armv8_3a,
-            builtin.Arch.armv8_2a,
-            builtin.Arch.armv8_1a,
-            builtin.Arch.armv8,
-            builtin.Arch.armv8r,
-            builtin.Arch.armv8m_baseline,
-            builtin.Arch.armv8m_mainline,
-            builtin.Arch.armv7,
-            builtin.Arch.armv7em,
-            builtin.Arch.armv7m,
-            builtin.Arch.armv7s,
-            builtin.Arch.armv7k,
-            builtin.Arch.armv7ve,
-            builtin.Arch.armv6,
-            builtin.Arch.armv6m,
-            builtin.Arch.armv6k,
-            builtin.Arch.armv6t2,
-            builtin.Arch.armv5,
-            builtin.Arch.armv5te,
-            builtin.Arch.armv4t,
+            builtin.Arch.arm,
             => return "arm",
             builtin.Arch.powerpc => return "ppc",
             builtin.Arch.powerpc64 => return "ppc64",
std/fmt/index.zig
@@ -1118,7 +1118,7 @@ test "fmt.format" {
         const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
         testing.expect(mem.eql(u8, result, "f64: nan\n"));
     }
-    if (builtin.arch != builtin.Arch.armv8) {
+    if (builtin.arch != builtin.Arch.arm) {
         // negative nan is not defined by IEE 754,
         // and ARM thus normalizes it to positive nan
         var buf1: [32]u8 = undefined;
std/os/linux/index.zig
@@ -6,7 +6,7 @@ const vdso = @import("vdso.zig");
 pub use switch (builtin.arch) {
     builtin.Arch.x86_64 => @import("x86_64.zig"),
     builtin.Arch.i386 => @import("i386.zig"),
-    builtin.Arch.aarch64v8 => @import("arm64.zig"),
+    builtin.Arch.aarch64 => @import("arm64.zig"),
     else => @compileError("unsupported arch"),
 };
 pub use @import("errno.zig");
std/special/compiler_rt/index.zig
@@ -184,60 +184,10 @@ const is_arm_64 = switch (builtin.arch) {
 };
 
 const is_arm_arch = switch (builtin.arch) {
-    builtin.Arch.armv8_3a,
-    builtin.Arch.armv8_2a,
-    builtin.Arch.armv8_1a,
-    builtin.Arch.armv8,
-    builtin.Arch.armv8r,
-    builtin.Arch.armv8m_baseline,
-    builtin.Arch.armv8m_mainline,
-    builtin.Arch.armv7,
-    builtin.Arch.armv7em,
-    builtin.Arch.armv7m,
-    builtin.Arch.armv7s,
-    builtin.Arch.armv7k,
-    builtin.Arch.armv7ve,
-    builtin.Arch.armv6,
-    builtin.Arch.armv6m,
-    builtin.Arch.armv6k,
-    builtin.Arch.armv6t2,
-    builtin.Arch.armv5,
-    builtin.Arch.armv5te,
-    builtin.Arch.armv4t,
-    builtin.Arch.armebv8_3a,
-    builtin.Arch.armebv8_2a,
-    builtin.Arch.armebv8_1a,
-    builtin.Arch.armebv8,
-    builtin.Arch.armebv8r,
-    builtin.Arch.armebv8m_baseline,
-    builtin.Arch.armebv8m_mainline,
-    builtin.Arch.armebv7,
-    builtin.Arch.armebv7em,
-    builtin.Arch.armebv7m,
-    builtin.Arch.armebv7s,
-    builtin.Arch.armebv7k,
-    builtin.Arch.armebv7ve,
-    builtin.Arch.armebv6,
-    builtin.Arch.armebv6m,
-    builtin.Arch.armebv6k,
-    builtin.Arch.armebv6t2,
-    builtin.Arch.armebv5,
-    builtin.Arch.armebv5te,
-    builtin.Arch.armebv4t,
-    builtin.Arch.aarch64v8_3a,
-    builtin.Arch.aarch64v8_2a,
-    builtin.Arch.aarch64v8_1a,
-    builtin.Arch.aarch64v8,
-    builtin.Arch.aarch64v8r,
-    builtin.Arch.aarch64v8m_baseline,
-    builtin.Arch.aarch64v8m_mainline,
-    builtin.Arch.aarch64_bev8_3a,
-    builtin.Arch.aarch64_bev8_2a,
-    builtin.Arch.aarch64_bev8_1a,
-    builtin.Arch.aarch64_bev8,
-    builtin.Arch.aarch64_bev8r,
-    builtin.Arch.aarch64_bev8m_baseline,
-    builtin.Arch.aarch64_bev8m_mainline,
+    builtin.Arch.arm,
+    builtin.Arch.armeb,
+    builtin.Arch.aarch64,
+    builtin.Arch.aarch64_be,
     builtin.Arch.thumb,
     builtin.Arch.thumbeb,
     => true,
std/special/bootstrap.zig
@@ -31,7 +31,7 @@ nakedcc fn _start() noreturn {
                 : [argc] "=r" (-> [*]usize)
             );
         },
-        builtin.Arch.aarch64v8 => {
+        builtin.Arch.aarch64, builtin.Arch.aarch64_be => {
             argc_ptr = asm ("mov %[argc], sp"
                 : [argc] "=r" (-> [*]usize)
             );
std/build.zig
@@ -731,20 +731,40 @@ const Version = struct {
 const CrossTarget = struct {
     arch: builtin.Arch,
     os: builtin.Os,
-    environ: builtin.Environ,
+    abi: builtin.Abi,
 };
 
 pub const Target = union(enum) {
     Native: void,
     Cross: CrossTarget,
 
+    fn archSubArchName(arch: builtin.Arch) []const u8 {
+        return switch (arch) {
+            builtin.Arch.arm => |sub| @tagName(sub),
+            builtin.Arch.armeb => |sub| @tagName(sub),
+            builtin.Arch.thumb => |sub| @tagName(sub),
+            builtin.Arch.thumbeb => |sub| @tagName(sub),
+            builtin.Arch.aarch64 => |sub| @tagName(sub),
+            builtin.Arch.aarch64_be => |sub| @tagName(sub),
+            builtin.Arch.kalimba => |sub| @tagName(sub),
+            else => "",
+        };
+    }
+
+    pub fn subArchName(self: Target) []const u8 {
+        switch (self) {
+            Target.Native => return archSubArchName(builtin.arch),
+            Target.Cross => |cross| return archSubArchName(cross.arch),
+        }
+    }
+
     pub fn oFileExt(self: *const Target) []const u8 {
-        const environ = switch (self.*) {
-            Target.Native => builtin.environ,
-            Target.Cross => |t| t.environ,
+        const abi = switch (self.*) {
+            Target.Native => builtin.abi,
+            Target.Cross => |t| t.abi,
         };
-        return switch (environ) {
-            builtin.Environ.msvc => ".obj",
+        return switch (abi) {
+            builtin.Abi.msvc => ".obj",
             else => ".o",
         };
     }
@@ -978,12 +998,17 @@ pub const LibExeObjStep = struct {
         }
     }
 
-    pub fn setTarget(self: *LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void {
+    pub fn setTarget(
+        self: *LibExeObjStep,
+        target_arch: builtin.Arch,
+        target_os: builtin.Os,
+        target_abi: builtin.Abi,
+    ) void {
         self.target = Target{
             .Cross = CrossTarget{
                 .arch = target_arch,
                 .os = target_os,
-                .environ = target_environ,
+                .abi = target_abi,
             },
         };
         self.computeOutFileNames();
@@ -1296,14 +1321,16 @@ pub const LibExeObjStep = struct {
         switch (self.target) {
             Target.Native => {},
             Target.Cross => |cross_target| {
-                zig_args.append("--target-arch") catch unreachable;
-                zig_args.append(@tagName(cross_target.arch)) catch unreachable;
-
-                zig_args.append("--target-os") catch unreachable;
-                zig_args.append(@tagName(cross_target.os)) catch unreachable;
-
-                zig_args.append("--target-environ") catch unreachable;
-                zig_args.append(@tagName(cross_target.environ)) catch unreachable;
+                const triple = builder.fmt(
+                    "{}{}-{}-{}",
+                    @tagName(cross_target.arch),
+                    Target.archSubArchName(cross_target.arch),
+                    @tagName(cross_target.os),
+                    @tagName(cross_target.abi),
+                );
+
+                try zig_args.append("-target");
+                try zig_args.append(triple);
             },
         }
 
test/tests.zig
@@ -24,24 +24,24 @@ const gen_h = @import("gen_h.zig");
 const TestTarget = struct {
     os: builtin.Os,
     arch: builtin.Arch,
-    environ: builtin.Environ,
+    abi: builtin.Abi,
 };
 
 const test_targets = []TestTarget{
     TestTarget{
         .os = builtin.Os.linux,
         .arch = builtin.Arch.x86_64,
-        .environ = builtin.Environ.gnu,
+        .abi = builtin.Abi.gnu,
     },
     TestTarget{
         .os = builtin.Os.macosx,
         .arch = builtin.Arch.x86_64,
-        .environ = builtin.Environ.unknown,
+        .abi = builtin.Abi.gnu,
     },
     TestTarget{
         .os = builtin.Os.windows,
         .arch = builtin.Arch.x86_64,
-        .environ = builtin.Environ.msvc,
+        .abi = builtin.Abi.msvc,
     },
 };
 
@@ -189,7 +189,7 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
                     these_tests.setFilter(test_filter);
                     these_tests.setBuildMode(mode);
                     if (!is_native) {
-                        these_tests.setTarget(test_target.arch, test_target.os, test_target.environ);
+                        these_tests.setTarget(test_target.arch, test_target.os, test_target.abi);
                     }
                     if (link_libc) {
                         these_tests.linkSystemLibrary("c");
README.md
@@ -77,39 +77,54 @@ clarity.
    - what sizes are the C integer types
    - C ABI calling convention for this target
    - bootstrap code and default panic handler
+ * `zig targets` is guaranteed to include this target.
 
 #### Tier 4 Support
 
  * Support for these targets is entirely experimental.
  * LLVM may have the target as an experimental target, which means that you
    need to use Zig-provided binaries for the target to be available, or
-   build LLVM from source with special configure flags.
+   build LLVM from source with special configure flags. `zig targets` will
+   display the target if it is available.
  * This target may be considered deprecated by an official party,
    [such as macosx/i386](https://support.apple.com/en-us/HT208436) in which
    case this target will remain forever stuck in Tier 4.
+ * This target may only support `--emit asm` and cannot emit object files.
 
 #### Support Table
 
-|        | freestanding | linux  | macosx | windows | freebsd | netbsd | UEFI   | other  |
-|--------|--------------|--------|--------|---------|---------|------- | -------|--------|
-|x86_64  | Tier 2       | Tier 1 | Tier 1 | Tier 1  | Tier 2  | Tier 2 | Tier 2 | Tier 3 |
-|i386    | Tier 2       | Tier 2 | Tier 4 | Tier 2  | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|arm     | Tier 2       | Tier 3 | Tier 3 | Tier 3  | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|arm64   | Tier 2       | Tier 2 | Tier 3 | Tier 3  | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|bpf     | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|hexagon | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|mips    | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|powerpc | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|r600    | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|amdgcn  | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|sparc   | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|s390x   | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|spir    | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|lanai   | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
-|wasm32  | Tier 4       | N/A    | N/A    | N/A     | N/A     | N/A    | N/A    | N/A    |
-|wasm64  | Tier 4       | N/A    | N/A    | N/A     | N/A     | N/A    | N/A    | N/A    |
-|riscv32 | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | Tier 4 | Tier 4 |
-|riscv64 | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | Tier 4 | Tier 4 |
+|             | freestanding | linux  | macosx | windows | freebsd | netbsd | UEFI   | other  |
+|-------------|--------------|--------|--------|---------|---------|------- | -------|--------|
+|x86_64       | Tier 2       | Tier 1 | Tier 1 | Tier 1  | Tier 2  | Tier 2 | Tier 2 | Tier 3 |
+|i386         | Tier 2       | Tier 2 | Tier 4 | Tier 2  | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
+|arm          | Tier 2       | Tier 3 | Tier 3 | Tier 3  | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
+|arm64        | Tier 2       | Tier 2 | Tier 3 | Tier 3  | Tier 3  | Tier 3 | Tier 3 | Tier 3 |
+|avr          | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | N/A    | Tier 3 |
+|bpf          | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | N/A    | Tier 3 |
+|hexagon      | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | N/A    | Tier 3 |
+|mips         | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | N/A    | Tier 3 |
+|powerpc      | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | N/A    | Tier 3 |
+|amdgcn       | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | N/A    | Tier 3 |
+|sparc        | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | N/A    | Tier 3 |
+|s390x        | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | N/A    | Tier 3 |
+|lanai        | Tier 3       | Tier 3 | N/A    | N/A     | Tier 3  | Tier 3 | N/A    | Tier 3 |
+|wasm32       | Tier 4       | N/A    | N/A    | N/A     | N/A     | N/A    | N/A    | N/A    |
+|wasm64       | Tier 4       | N/A    | N/A    | N/A     | N/A     | N/A    | N/A    | N/A    |
+|riscv32      | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | Tier 4 | Tier 4 |
+|riscv64      | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | Tier 4 | Tier 4 |
+|xcore        | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|nvptx        | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|msp430       | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|r600         | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|arc          | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|tce          | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|le           | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|amdil        | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|hsail        | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|spir         | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|kalimba      | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|shave        | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
+|renderscript | Tier 4       | Tier 4 | N/A    | N/A     | Tier 4  | Tier 4 | N/A    | Tier 4 |
 
 ## Community