[WebAssembly] Add -i128:128 to the datalayout string. (#119204)

Clang [defaults to aligning `__int128_t` to 16 bytes], while LLVM
`datalayout` strings [default to aligning `i128` to 8 bytes]. Wasm is
currently using the defaults for both, so it's inconsistent. Fix this by
adding `-i128:128` to Wasm's `datalayout` string so that it aligns
`i128` to 16 bytes too.

This is similar to
[llvm/llvm-project@dbad963](dbad963a69)
for SPARC.

This fixes rust-lang/rust#133991; see that issue for further discussion.

[defaults to aligning `__int128_t` to 16 bytes]:
f8b4182f07/clang/lib/Basic/TargetInfo.cpp (L77)
[default to aligning `i128` to 8 bytes]:
https://llvm.org/docs/LangRef.html#langref-datalayout
This commit is contained in:
Dan Gohman
2024-12-10 09:21:58 -08:00
committed by GitHub
parent c7634c1b61
commit c5ab70c508
7 changed files with 148 additions and 125 deletions

View File

@@ -183,11 +183,12 @@ public:
const TargetOptions &Opts)
: WebAssemblyTargetInfo(T, Opts) {
if (T.isOSEmscripten())
resetDataLayout("e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-"
"S128-ni:1:10:20");
else
resetDataLayout(
"e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20");
"e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-f128:64-n32:64-"
"S128-ni:1:10:20");
else
resetDataLayout("e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-"
"S128-ni:1:10:20");
}
protected:
@@ -207,11 +208,12 @@ public:
PtrDiffType = SignedLong;
IntPtrType = SignedLong;
if (T.isOSEmscripten())
resetDataLayout("e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-"
"S128-ni:1:10:20");
else
resetDataLayout(
"e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20");
"e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-f128:64-n32:64-"
"S128-ni:1:10:20");
else
resetDataLayout("e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-"
"S128-ni:1:10:20");
}
protected:

View File

@@ -108,11 +108,11 @@
// RUN: %clang_cc1 -triple wasm32-unknown-unknown -o - -emit-llvm %s | \
// RUN: FileCheck %s -check-prefix=WEBASSEMBLY32
// WEBASSEMBLY32: target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
// WEBASSEMBLY32: target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
// RUN: %clang_cc1 -triple wasm64-unknown-unknown -o - -emit-llvm %s | \
// RUN: FileCheck %s -check-prefix=WEBASSEMBLY64
// WEBASSEMBLY64: target datalayout = "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
// WEBASSEMBLY64: target datalayout = "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
// RUN: %clang_cc1 -triple lanai-unknown-unknown -o - -emit-llvm %s | \
// RUN: FileCheck %s -check-prefix=LANAI

View File

@@ -5559,7 +5559,8 @@ std::string llvm::UpgradeDataLayoutString(StringRef DL, StringRef TT) {
return Res;
}
if (T.isSPARC() || (T.isMIPS64() && !DL.contains("m:m")) || T.isPPC64()) {
if (T.isSPARC() || (T.isMIPS64() && !DL.contains("m:m")) || T.isPPC64() ||
T.isWasm()) {
// Mips64 with o32 ABI did not add "-i128:128".
// Add "-i128:128"
std::string I64 = "-i64:64";

View File

@@ -118,13 +118,13 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine(
T,
TT.isArch64Bit()
? (TT.isOSEmscripten() ? "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-"
"f128:64-n32:64-S128-ni:1:10:20"
"i128:128-f128:64-n32:64-S128-ni:1:10:20"
: "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-"
"n32:64-S128-ni:1:10:20")
"i128:128-n32:64-S128-ni:1:10:20")
: (TT.isOSEmscripten() ? "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-"
"f128:64-n32:64-S128-ni:1:10:20"
"i128:128-f128:64-n32:64-S128-ni:1:10:20"
: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-"
"n32:64-S128-ni:1:10:20"),
"i128:128-n32:64-S128-ni:1:10:20"),
TT, CPU, FS, Options, getEffectiveRelocModel(RM, TT),
getEffectiveCodeModel(CM, CodeModel::Large), OL),
TLOF(new WebAssemblyTargetObjectFile()),

View File

@@ -0,0 +1,26 @@
; RUN: llc < %s -march=wasm32 | FileCheck %s
; RUN: llc < %s -march=wasm64 | FileCheck %s
; CHECK: .Li8:
; CHECK-DAG: .size .Li8, 1
@i8 = private constant i8 42
; CHECK: .p2align 1
; CHECK-NEXT: .Li16:
; CHECK-DAG: .size .Li16, 2
@i16 = private constant i16 42
; CHECK: .p2align 2
; CHECK-NEXT: .Li32:
; CHECK-DAG: .size .Li32, 4
@i32 = private constant i32 42
; CHECK: .p2align 3
; CHECK-NEXT: .Li64:
; CHECK-DAG: .size .Li64, 8
@i64 = private constant i64 42
; CHECK: .p2align 4
; CHECK-NEXT: .Li128:
; CHECK-DAG: .size .Li128, 16
@i128 = private constant i128 42

View File

@@ -13,18 +13,16 @@ define i64 @test0() {
; CHECK: .functype test0 () -> (i64)
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: global.get $push0=, __stack_pointer
; CHECK-NEXT: i32.const $push1=, 80
; CHECK-NEXT: i32.sub $push7=, $pop0, $pop1
; CHECK-NEXT: local.tee $push6=, $1=, $pop7
; CHECK-NEXT: global.set __stack_pointer, $pop6
; CHECK-NEXT: i32.const $push4=, 8
; CHECK-NEXT: i32.add $push5=, $1, $pop4
; CHECK-NEXT: call return_multi_multi, $pop5
; CHECK-NEXT: i64.load $0=, 8($1)
; CHECK-NEXT: i32.const $push2=, 80
; CHECK-NEXT: i32.const $push1=, 96
; CHECK-NEXT: i32.sub $push5=, $pop0, $pop1
; CHECK-NEXT: local.tee $push4=, $1=, $pop5
; CHECK-NEXT: global.set __stack_pointer, $pop4
; CHECK-NEXT: call return_multi_multi, $1
; CHECK-NEXT: i64.load $0=, 0($1)
; CHECK-NEXT: i32.const $push2=, 96
; CHECK-NEXT: i32.add $push3=, $1, $pop2
; CHECK-NEXT: global.set __stack_pointer, $pop3
; CHECK-NEXT: local.copy $push8=, $0
; CHECK-NEXT: local.copy $push6=, $0
; CHECK-NEXT: # fallthrough-return
%t0 = call { i64, i128, i192, i128, i64 } @return_multi_multi()
%t1 = extractvalue { i64, i128, i192, i128, i64 } %t0, 0
@@ -36,18 +34,16 @@ define i128 @test1() {
; CHECK: .functype test1 (i32) -> ()
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: global.get $push1=, __stack_pointer
; CHECK-NEXT: i32.const $push2=, 80
; CHECK-NEXT: i32.sub $push8=, $pop1, $pop2
; CHECK-NEXT: local.tee $push7=, $2=, $pop8
; CHECK-NEXT: global.set __stack_pointer, $pop7
; CHECK-NEXT: i32.const $push5=, 8
; CHECK-NEXT: i32.add $push6=, $2, $pop5
; CHECK-NEXT: call return_multi_multi, $pop6
; CHECK-NEXT: i64.load $1=, 16($2)
; CHECK-NEXT: i64.load $push0=, 24($2)
; CHECK-NEXT: i64.store 8($0), $pop0
; CHECK-NEXT: i64.store 0($0), $1
; CHECK-NEXT: i32.const $push3=, 80
; CHECK-NEXT: i32.const $push2=, 96
; CHECK-NEXT: i32.sub $push6=, $pop1, $pop2
; CHECK-NEXT: local.tee $push5=, $2=, $pop6
; CHECK-NEXT: global.set __stack_pointer, $pop5
; CHECK-NEXT: call return_multi_multi, $2
; CHECK-NEXT: i64.load $1=, 24($2)
; CHECK-NEXT: i64.load $push0=, 16($2)
; CHECK-NEXT: i64.store 0($0), $pop0
; CHECK-NEXT: i64.store 8($0), $1
; CHECK-NEXT: i32.const $push3=, 96
; CHECK-NEXT: i32.add $push4=, $2, $pop3
; CHECK-NEXT: global.set __stack_pointer, $pop4
; CHECK-NEXT: # fallthrough-return
@@ -61,13 +57,11 @@ define i192 @test2() {
; CHECK: .functype test2 (i32) -> ()
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: global.get $push3=, __stack_pointer
; CHECK-NEXT: i32.const $push4=, 80
; CHECK-NEXT: i32.sub $push10=, $pop3, $pop4
; CHECK-NEXT: local.tee $push9=, $3=, $pop10
; CHECK-NEXT: global.set __stack_pointer, $pop9
; CHECK-NEXT: i32.const $push7=, 8
; CHECK-NEXT: i32.add $push8=, $3, $pop7
; CHECK-NEXT: call return_multi_multi, $pop8
; CHECK-NEXT: i32.const $push4=, 96
; CHECK-NEXT: i32.sub $push8=, $pop3, $pop4
; CHECK-NEXT: local.tee $push7=, $3=, $pop8
; CHECK-NEXT: global.set __stack_pointer, $pop7
; CHECK-NEXT: call return_multi_multi, $3
; CHECK-NEXT: i64.load $1=, 40($3)
; CHECK-NEXT: i64.load $2=, 32($3)
; CHECK-NEXT: i32.const $push0=, 48
@@ -76,7 +70,7 @@ define i192 @test2() {
; CHECK-NEXT: i64.store 16($0), $pop2
; CHECK-NEXT: i64.store 0($0), $2
; CHECK-NEXT: i64.store 8($0), $1
; CHECK-NEXT: i32.const $push5=, 80
; CHECK-NEXT: i32.const $push5=, 96
; CHECK-NEXT: i32.add $push6=, $3, $pop5
; CHECK-NEXT: global.set __stack_pointer, $pop6
; CHECK-NEXT: # fallthrough-return
@@ -90,18 +84,16 @@ define i128 @test3() {
; CHECK: .functype test3 (i32) -> ()
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: global.get $push1=, __stack_pointer
; CHECK-NEXT: i32.const $push2=, 80
; CHECK-NEXT: i32.sub $push8=, $pop1, $pop2
; CHECK-NEXT: local.tee $push7=, $2=, $pop8
; CHECK-NEXT: global.set __stack_pointer, $pop7
; CHECK-NEXT: i32.const $push5=, 8
; CHECK-NEXT: i32.add $push6=, $2, $pop5
; CHECK-NEXT: call return_multi_multi, $pop6
; CHECK-NEXT: i64.load $1=, 56($2)
; CHECK-NEXT: i32.const $push2=, 96
; CHECK-NEXT: i32.sub $push6=, $pop1, $pop2
; CHECK-NEXT: local.tee $push5=, $2=, $pop6
; CHECK-NEXT: global.set __stack_pointer, $pop5
; CHECK-NEXT: call return_multi_multi, $2
; CHECK-NEXT: i64.load $1=, 72($2)
; CHECK-NEXT: i64.load $push0=, 64($2)
; CHECK-NEXT: i64.store 8($0), $pop0
; CHECK-NEXT: i64.store 0($0), $1
; CHECK-NEXT: i32.const $push3=, 80
; CHECK-NEXT: i64.store 0($0), $pop0
; CHECK-NEXT: i64.store 8($0), $1
; CHECK-NEXT: i32.const $push3=, 96
; CHECK-NEXT: i32.add $push4=, $2, $pop3
; CHECK-NEXT: global.set __stack_pointer, $pop4
; CHECK-NEXT: # fallthrough-return
@@ -115,18 +107,16 @@ define i64 @test4() {
; CHECK: .functype test4 () -> (i64)
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: global.get $push0=, __stack_pointer
; CHECK-NEXT: i32.const $push1=, 80
; CHECK-NEXT: i32.sub $push7=, $pop0, $pop1
; CHECK-NEXT: local.tee $push6=, $1=, $pop7
; CHECK-NEXT: global.set __stack_pointer, $pop6
; CHECK-NEXT: i32.const $push4=, 8
; CHECK-NEXT: i32.add $push5=, $1, $pop4
; CHECK-NEXT: call return_multi_multi, $pop5
; CHECK-NEXT: i64.load $0=, 72($1)
; CHECK-NEXT: i32.const $push2=, 80
; CHECK-NEXT: i32.const $push1=, 96
; CHECK-NEXT: i32.sub $push5=, $pop0, $pop1
; CHECK-NEXT: local.tee $push4=, $1=, $pop5
; CHECK-NEXT: global.set __stack_pointer, $pop4
; CHECK-NEXT: call return_multi_multi, $1
; CHECK-NEXT: i64.load $0=, 80($1)
; CHECK-NEXT: i32.const $push2=, 96
; CHECK-NEXT: i32.add $push3=, $1, $pop2
; CHECK-NEXT: global.set __stack_pointer, $pop3
; CHECK-NEXT: local.copy $push8=, $0
; CHECK-NEXT: local.copy $push6=, $0
; CHECK-NEXT: # fallthrough-return
%t0 = call { i64, i128, i192, i128, i64 } @return_multi_multi()
%t1 = extractvalue { i64, i128, i192, i128, i64 } %t0, 4
@@ -140,20 +130,18 @@ define { i64, i128 } @test5() {
; CHECK: .functype test5 (i32) -> ()
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: global.get $push1=, __stack_pointer
; CHECK-NEXT: i32.const $push2=, 80
; CHECK-NEXT: i32.sub $push8=, $pop1, $pop2
; CHECK-NEXT: local.tee $push7=, $3=, $pop8
; CHECK-NEXT: global.set __stack_pointer, $pop7
; CHECK-NEXT: i32.const $push5=, 8
; CHECK-NEXT: i32.add $push6=, $3, $pop5
; CHECK-NEXT: call return_multi_multi, $pop6
; CHECK-NEXT: i64.load $1=, 8($3)
; CHECK-NEXT: i64.load $2=, 24($3)
; CHECK-NEXT: i64.load $push0=, 16($3)
; CHECK-NEXT: i64.store 8($0), $pop0
; CHECK-NEXT: i32.const $push2=, 96
; CHECK-NEXT: i32.sub $push6=, $pop1, $pop2
; CHECK-NEXT: local.tee $push5=, $3=, $pop6
; CHECK-NEXT: global.set __stack_pointer, $pop5
; CHECK-NEXT: call return_multi_multi, $3
; CHECK-NEXT: i64.load $1=, 24($3)
; CHECK-NEXT: i64.load $2=, 16($3)
; CHECK-NEXT: i64.load $push0=, 0($3)
; CHECK-NEXT: i64.store 0($0), $pop0
; CHECK-NEXT: i64.store 16($0), $2
; CHECK-NEXT: i64.store 0($0), $1
; CHECK-NEXT: i32.const $push3=, 80
; CHECK-NEXT: i64.store 24($0), $1
; CHECK-NEXT: i32.const $push3=, 96
; CHECK-NEXT: i32.add $push4=, $3, $pop3
; CHECK-NEXT: global.set __stack_pointer, $pop4
; CHECK-NEXT: # fallthrough-return
@@ -170,22 +158,20 @@ define { i128, i128 } @test6() {
; CHECK: .functype test6 (i32) -> ()
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: global.get $push1=, __stack_pointer
; CHECK-NEXT: i32.const $push2=, 80
; CHECK-NEXT: i32.sub $push8=, $pop1, $pop2
; CHECK-NEXT: local.tee $push7=, $4=, $pop8
; CHECK-NEXT: global.set __stack_pointer, $pop7
; CHECK-NEXT: i32.const $push5=, 8
; CHECK-NEXT: i32.add $push6=, $4, $pop5
; CHECK-NEXT: call return_multi_multi, $pop6
; CHECK-NEXT: i32.const $push2=, 96
; CHECK-NEXT: i32.sub $push6=, $pop1, $pop2
; CHECK-NEXT: local.tee $push5=, $4=, $pop6
; CHECK-NEXT: global.set __stack_pointer, $pop5
; CHECK-NEXT: call return_multi_multi, $4
; CHECK-NEXT: i64.load $1=, 24($4)
; CHECK-NEXT: i64.load $2=, 16($4)
; CHECK-NEXT: i64.load $3=, 64($4)
; CHECK-NEXT: i64.load $push0=, 56($4)
; CHECK-NEXT: i64.load $3=, 72($4)
; CHECK-NEXT: i64.load $push0=, 64($4)
; CHECK-NEXT: i64.store 16($0), $pop0
; CHECK-NEXT: i64.store 24($0), $3
; CHECK-NEXT: i64.store 0($0), $2
; CHECK-NEXT: i64.store 8($0), $1
; CHECK-NEXT: i32.const $push3=, 80
; CHECK-NEXT: i32.const $push3=, 96
; CHECK-NEXT: i32.add $push4=, $4, $pop3
; CHECK-NEXT: global.set __stack_pointer, $pop4
; CHECK-NEXT: # fallthrough-return
@@ -202,24 +188,22 @@ define { i64, i192 } @test7() {
; CHECK: .functype test7 (i32) -> ()
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: global.get $push3=, __stack_pointer
; CHECK-NEXT: i32.const $push4=, 80
; CHECK-NEXT: i32.sub $push10=, $pop3, $pop4
; CHECK-NEXT: local.tee $push9=, $4=, $pop10
; CHECK-NEXT: global.set __stack_pointer, $pop9
; CHECK-NEXT: i32.const $push7=, 8
; CHECK-NEXT: i32.add $push8=, $4, $pop7
; CHECK-NEXT: call return_multi_multi, $pop8
; CHECK-NEXT: i64.load $1=, 8($4)
; CHECK-NEXT: i64.load $2=, 40($4)
; CHECK-NEXT: i64.load $3=, 32($4)
; CHECK-NEXT: i32.const $push4=, 96
; CHECK-NEXT: i32.sub $push8=, $pop3, $pop4
; CHECK-NEXT: local.tee $push7=, $4=, $pop8
; CHECK-NEXT: global.set __stack_pointer, $pop7
; CHECK-NEXT: call return_multi_multi, $4
; CHECK-NEXT: i32.const $push0=, 48
; CHECK-NEXT: i32.add $push1=, $4, $pop0
; CHECK-NEXT: i64.load $push2=, 0($pop1)
; CHECK-NEXT: i64.store 24($0), $pop2
; CHECK-NEXT: i64.store 8($0), $3
; CHECK-NEXT: i64.store 16($0), $2
; CHECK-NEXT: i64.store 0($0), $1
; CHECK-NEXT: i32.const $push5=, 80
; CHECK-NEXT: i64.load $1=, 0($pop1)
; CHECK-NEXT: i64.load $2=, 40($4)
; CHECK-NEXT: i64.load $3=, 32($4)
; CHECK-NEXT: i64.load $push2=, 0($4)
; CHECK-NEXT: i64.store 0($0), $pop2
; CHECK-NEXT: i64.store 32($0), $1
; CHECK-NEXT: i64.store 16($0), $3
; CHECK-NEXT: i64.store 24($0), $2
; CHECK-NEXT: i32.const $push5=, 96
; CHECK-NEXT: i32.add $push6=, $4, $pop5
; CHECK-NEXT: global.set __stack_pointer, $pop6
; CHECK-NEXT: # fallthrough-return
@@ -236,32 +220,30 @@ define { i128, i192, i128, i64 } @test8() {
; CHECK: .functype test8 (i32) -> ()
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: global.get $push3=, __stack_pointer
; CHECK-NEXT: i32.const $push4=, 80
; CHECK-NEXT: i32.sub $push10=, $pop3, $pop4
; CHECK-NEXT: local.tee $push9=, $8=, $pop10
; CHECK-NEXT: global.set __stack_pointer, $pop9
; CHECK-NEXT: i32.const $push7=, 8
; CHECK-NEXT: i32.add $push8=, $8, $pop7
; CHECK-NEXT: call return_multi_multi, $pop8
; CHECK-NEXT: i32.const $push4=, 96
; CHECK-NEXT: i32.sub $push8=, $pop3, $pop4
; CHECK-NEXT: local.tee $push7=, $8=, $pop8
; CHECK-NEXT: global.set __stack_pointer, $pop7
; CHECK-NEXT: call return_multi_multi, $8
; CHECK-NEXT: i32.const $push0=, 48
; CHECK-NEXT: i32.add $push1=, $8, $pop0
; CHECK-NEXT: i64.load $1=, 0($pop1)
; CHECK-NEXT: i64.load $2=, 8($8)
; CHECK-NEXT: i64.load $2=, 72($8)
; CHECK-NEXT: i64.load $3=, 64($8)
; CHECK-NEXT: i64.load $4=, 56($8)
; CHECK-NEXT: i64.load $5=, 40($8)
; CHECK-NEXT: i64.load $6=, 32($8)
; CHECK-NEXT: i64.load $7=, 24($8)
; CHECK-NEXT: i64.load $push2=, 16($8)
; CHECK-NEXT: i64.store 40($0), $pop2
; CHECK-NEXT: i64.load $4=, 40($8)
; CHECK-NEXT: i64.load $5=, 32($8)
; CHECK-NEXT: i64.load $6=, 24($8)
; CHECK-NEXT: i64.load $7=, 16($8)
; CHECK-NEXT: i64.load $push2=, 0($8)
; CHECK-NEXT: i64.store 64($0), $pop2
; CHECK-NEXT: i64.store 48($0), $7
; CHECK-NEXT: i64.store 56($0), $6
; CHECK-NEXT: i64.store 32($0), $1
; CHECK-NEXT: i64.store 16($0), $6
; CHECK-NEXT: i64.store 24($0), $5
; CHECK-NEXT: i64.store 0($0), $4
; CHECK-NEXT: i64.store 8($0), $3
; CHECK-NEXT: i64.store 56($0), $2
; CHECK-NEXT: i32.const $push5=, 80
; CHECK-NEXT: i64.store 16($0), $5
; CHECK-NEXT: i64.store 24($0), $4
; CHECK-NEXT: i64.store 0($0), $3
; CHECK-NEXT: i64.store 8($0), $2
; CHECK-NEXT: i32.const $push5=, 96
; CHECK-NEXT: i32.add $push6=, $8, $pop5
; CHECK-NEXT: global.set __stack_pointer, $pop6
; CHECK-NEXT: # fallthrough-return

View File

@@ -102,6 +102,18 @@ TEST(DataLayoutUpgradeTest, ValidDataLayoutUpgrade) {
UpgradeDataLayoutString("E-m:a-Fi64-i64:64-n32:64", "powerpc64-ibm-aix"),
"E-m:a-Fi64-i64:64-i128:128-n32:64");
// Check that WebAssembly targets add -i128:128.
EXPECT_EQ(
UpgradeDataLayoutString(
"e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20",
"wasm32"),
"e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20");
EXPECT_EQ(
UpgradeDataLayoutString(
"e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20",
"wasm64"),
"e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20");
// Check that SPIR && SPIRV targets add -G1 if it's not present.
EXPECT_EQ(UpgradeDataLayoutString("e-p:32:32", "spir"), "e-p:32:32-G1");
EXPECT_EQ(UpgradeDataLayoutString("e-p:32:32", "spir64"), "e-p:32:32-G1");