diff --git a/lld/test/wasm/data-segments.ll b/lld/test/wasm/data-segments.ll --- a/lld/test/wasm/data-segments.ll +++ b/lld/test/wasm/data-segments.ll @@ -1,253 +1,262 @@ -; RUN: llc --mtriple=wasm32-unknown-unknown -filetype=obj %s -o %t.atomics.o -mattr=+atomics -; RUN: llc --mtriple=wasm32-unknown-unknown -filetype=obj %s -o %t.bulk-mem.o -mattr=+bulk-memory -; RUN: llc --mtriple=wasm64-unknown-unknown -filetype=obj %s -o %t.bulk-mem64.o -mattr=+bulk-memory -; RUN: llc --mtriple=wasm32-unknown-unknown -filetype=obj %s -o %t.atomics.bulk-mem.o -mattr=+atomics,+bulk-memory -; RUN: llc --mtriple=wasm64-unknown-unknown -filetype=obj %s -o %t.atomics.bulk-mem64.o -mattr=+atomics,+bulk-memory -; RUN: llc --mtriple=wasm32-unknown-unknown -filetype=obj %s -o %t.atomics.bulk-mem.pic.o -relocation-model=pic -mattr=+atomics,+bulk-memory,+mutable-globals -; RUN: llc --mtriple=wasm64-unknown-unknown -filetype=obj %s -o %t.atomics.bulk-mem.pic-mem64.o -relocation-model=pic -mattr=+atomics,+bulk-memory,+mutable-globals - -; atomics, shared memory => error -; RUN: not wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.o -o %t.atomics.wasm 2>&1 | FileCheck %s --check-prefix ERROR - -; bulk memory, unshared memory => active segments -; RUN: wasm-ld -no-gc-sections --no-entry %t.bulk-mem.o -o %t.bulk-mem.wasm -; RUN: obj2yaml %t.bulk-mem.wasm | FileCheck %s --check-prefixes ACTIVE,ACTIVE32 - -; bulk memory, unshared memory, wasm64 => active segments -; RUN: wasm-ld -mwasm64 -no-gc-sections --no-entry %t.bulk-mem64.o -o %t.bulk-mem64.wasm -; RUN: obj2yaml %t.bulk-mem64.wasm | FileCheck %s --check-prefixes ACTIVE,ACTIVE64 - -; atomics, bulk memory, shared memory => passive segments -; RUN: wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem.o -o %t.atomics.bulk-mem.wasm -; RUN: obj2yaml %t.atomics.bulk-mem.wasm | FileCheck %s --check-prefix PASSIVE -; RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_init_memory --no-show-raw-insn --no-leading-addr %t.atomics.bulk-mem.wasm | FileCheck %s --check-prefixes DIS,NOPIC-DIS -DPTR=i32 - -; atomics, bulk memory, shared memory, wasm64 => passive segments -; RUN: wasm-ld -mwasm64 -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem64.o -o %t.atomics.bulk-mem64.wasm -; RUN: obj2yaml %t.atomics.bulk-mem64.wasm | FileCheck %s --check-prefix PASSIVE -; RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_init_memory --no-show-raw-insn --no-leading-addr %t.atomics.bulk-mem64.wasm | FileCheck %s --check-prefixes DIS,NOPIC-DIS -DPTR=i64 - -; Also test in combination with PIC/pie -; RUN: wasm-ld --experimental-pic -pie -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem.pic.o -o %t.pic.wasm -; RUN: obj2yaml %t.pic.wasm | FileCheck %s --check-prefixes PASSIVE-PIC,PASSIVE32-PIC -; RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs,__wasm_init_memory --no-show-raw-insn --no-leading-addr %t.pic.wasm | FileCheck %s --check-prefixes DIS,PIC-DIS -DPTR=i32 - -; Also test in combination with PIC/pie + wasm64 -; RUN: wasm-ld -mwasm64 --experimental-pic -pie -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem.pic-mem64.o -o %t.pic-mem64.wasm -; RUN: obj2yaml %t.pic-mem64.wasm | FileCheck %s --check-prefixes PASSIVE-PIC,PASSIVE64-PIC -; RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs,__wasm_init_memory --no-show-raw-insn --no-leading-addr %t.pic-mem64.wasm | FileCheck %s --check-prefixes DIS,PIC-DIS -DPTR=i64 - -@a = hidden global [6 x i8] c"hello\00", align 1 -@b = hidden global [8 x i8] c"goodbye\00", align 1 -@c = hidden global [10000 x i8] zeroinitializer, align 1 -@d = hidden global i32 42, align 4 - -@e = private constant [9 x i8] c"constant\00", align 1 -@f = private constant i8 43, align 4 - -@g = thread_local global i32 99, align 4 - -; ERROR: 'bulk-memory' feature must be used in order to use shared memory - -; ACTIVE-LABEL: - Type: CODE -; ACTIVE-NEXT: Functions: -; ACTIVE-NEXT: - Index: 0 -; ACTIVE-NEXT: Locals: [] -; ACTIVE-NEXT: Body: 0B -; ACTIVE-NEXT: - Type: DATA -; ACTIVE-NEXT: Segments: -; ACTIVE-NEXT: - SectionOffset: 7 -; ACTIVE-NEXT: InitFlags: 0 -; ACTIVE-NEXT: Offset: -; ACTIVE32-NEXT: Opcode: I32_CONST -; ACTIVE64-NEXT: Opcode: I64_CONST -; ACTIVE-NEXT: Value: 1024 -; ACTIVE-NEXT: Content: 636F6E7374616E74000000002B -; ACTIVE-NEXT: - SectionOffset: 26 -; ACTIVE-NEXT: InitFlags: 0 -; ACTIVE-NEXT: Offset: -; ACTIVE32-NEXT: Opcode: I32_CONST -; ACTIVE64-NEXT: Opcode: I64_CONST -; ACTIVE-NEXT: Value: 1040 -; ACTIVE-NEXT: Content: 68656C6C6F00676F6F646279650000002A000000 -; ACTIVE-NEXT: - Type: CUSTOM -; ACTIVE-NEXT: Name: name -; ACTIVE-NEXT: FunctionNames: -; ACTIVE-NEXT: - Index: 0 -; ACTIVE-NEXT: Name: __wasm_call_ctors - -; PASSIVE-LABEL: - Type: START -; PASSIVE-NEXT: StartFunction: 2 -; PASSIVE-LABEL: - Type: DATACOUNT -; PASSIVE-NEXT: Count: 3 -; PASSIVE-LABEL: - Type: CODE -; PASSIVE-NEXT: Functions: -; PASSIVE-NEXT: - Index: 0 -; PASSIVE-NEXT: Locals: [] -; PASSIVE-NEXT: Body: 0B -; PASSIVE-NEXT: - Index: 1 -; PASSIVE-NEXT: Locals: [] -; PASSIVE-NEXT: Body: {{.*}} -; PASSIVE-NEXT: - Index: 2 -; PASSIVE-NEXT: Locals: [] -; PASSIVE-NEXT: Body: {{.*}} -; PASSIVE-NEXT: - Type: DATA -; PASSIVE-NEXT: Segments: -; PASSIVE-NEXT: - SectionOffset: 3 -; PASSIVE-NEXT: InitFlags: 1 -; PASSIVE-NEXT: Content: '63000000' -; PASSIVE-NEXT: - SectionOffset: 9 -; PASSIVE-NEXT: InitFlags: 1 -; PASSIVE-NEXT: Content: 636F6E7374616E74000000002B -; PASSIVE-NEXT: - SectionOffset: 24 -; PASSIVE-NEXT: InitFlags: 1 -; PASSIVE-NEXT: Content: 68656C6C6F00676F6F646279650000002A000000 -; PASSIVE-NEXT: - Type: CUSTOM -; PASSIVE-NEXT: Name: name -; PASSIVE-NEXT: FunctionNames: -; PASSIVE-NEXT: - Index: 0 -; PASSIVE-NEXT: Name: __wasm_call_ctors -; PASSIVE-NEXT: - Index: 1 -; PASSIVE-NEXT: Name: __wasm_init_tls -; PASSIVE-NEXT: - Index: 2 -; PASSIVE-NEXT: Name: __wasm_init_memory - -; PASSIVE-PIC: - Type: START -; PASSIVE-PIC-NEXT: StartFunction: 3 -; PASSIVE-PIC-NEXT: - Type: DATACOUNT -; PASSIVE-PIC-NEXT: Count: 3 -; PASSIVE-PIC-NEXT: - Type: CODE -; PASSIVE-PIC-NEXT: Functions: -; PASSIVE-PIC-NEXT: - Index: 0 -; PASSIVE-PIC-NEXT: Locals: [] -; PASSIVE-PIC-NEXT: Body: 0B -; PASSIVE-PIC-NEXT: - Index: 1 -; PASSIVE-PIC-NEXT: Locals: [] -; PASSIVE-PIC-NEXT: Body: {{.*}} -; PASSIVE-PIC-NEXT: - Index: 2 -; PASSIVE-PIC-NEXT: Locals: [] -; PASSIVE-PIC-NEXT: Body: 0B -; PASSIVE-PIC-NEXT: - Index: 3 -; PASSIVE-PIC-NEXT: Locals: -; PASSIVE32-PIC-NEXT: - Type: I32 -; PASSIVE64-PIC-NEXT: - Type: I64 -; PASSIVE-PIC-NEXT: Count: 2 -; PASSIVE-PIC-NEXT: Body: {{.*}} -; PASSIVE-PIC-NEXT: - Type: DATA -; PASSIVE-PIC-NEXT: Segments: -; PASSIVE-PIC-NEXT: - SectionOffset: 3 -; PASSIVE-PIC-NEXT: InitFlags: 1 -; PASSIVE-PIC-NEXT: Content: '63000000' -; PASSIVE-PIC-NEXT: - SectionOffset: 9 -; PASSIVE-PIC-NEXT: InitFlags: 1 -; PASSIVE-PIC-NEXT: Content: 636F6E7374616E74000000002B -; PASSIVE-PIC-NEXT: - SectionOffset: 24 -; PASSIVE-PIC-NEXT: InitFlags: 1 -; PASSIVE-PIC-NEXT: Content: 68656C6C6F00676F6F646279650000002A000000 -; PASSIVE-PIC-NEXT: - Type: CUSTOM -; PASSIVE-PIC-NEXT: Name: name -; PASSIVE-PIC-NEXT: FunctionNames: -; PASSIVE-PIC-NEXT: - Index: 0 -; PASSIVE-PIC-NEXT: Name: __wasm_call_ctors -; PASSIVE-PIC-NEXT: - Index: 1 -; PASSIVE-PIC-NEXT: Name: __wasm_init_tls -; PASSIVE-PIC-NEXT: - Index: 2 -; PASSIVE-PIC-NEXT: Name: __wasm_apply_data_relocs -; PASSIVE-PIC-NEXT: - Index: 3 -; PASSIVE-PIC-NEXT: Name: __wasm_init_memory - -; no data relocations. -; DIS-LABEL: <__wasm_call_ctors>: -; DIS-EMPTY: -; DIS-NEXT: end - -; In PIC mode __wasm_apply_data_relocs is export seperatly to __wasm_call_ctors -; PIC-DIS: <__wasm_apply_data_relocs>: -; PIC-DIS-EMPTY: - -; DIS-LABEL: <__wasm_init_memory>: - -; PIC-DIS: .local [[PTR]] -; PIC-DIS-NEXT: global.get 1 -; PIC-DIS-NEXT: [[PTR]].const 10040 -; PIC-DIS-NEXT: [[PTR]].add -; PIC-DIS-NEXT: local.set 0 - -; DIS: block -; DIS-NEXT: block -; DIS-NEXT: block - -; NOPIC-DIS-NEXT: [[PTR]].const 11064 -; PIC-DIS-NEXT: local.get 0 - -; DIS-NEXT: i32.const 0 -; DIS-NEXT: i32.const 1 -; DIS-NEXT: i32.atomic.rmw.cmpxchg 0 -; DIS-NEXT: br_table {0, 1, 2} # 1: down to label1 -; DIS-NEXT: # 2: down to label0 -; DIS-NEXT: end - -; NOPIC-DIS-NEXT: [[PTR]].const 1024 -; NOPIC-DIS-NEXT: [[PTR]].const 1024 -; NOPIC-DIS-NEXT: global.set 1 -; PIC-DIS-NEXT: [[PTR]].const 0 -; PIC-DIS-NEXT: global.get 1 -; PIC-DIS-NEXT: [[PTR]].add -; PIC-DIS-NEXT: local.tee 1 -; PIC-DIS-NEXT: global.set {{\d*}} -; PIC-DIS-NEXT: local.get 1 -; DIS-NEXT: i32.const 0 -; DIS-NEXT: i32.const 4 -; DIS-NEXT: memory.init 0, 0 - -; NOPIC-DIS-NEXT: [[PTR]].const 1028 -; PIC-DIS-NEXT: [[PTR]].const 4 -; PIC-DIS-NEXT: global.get 1 -; PIC-DIS-NEXT: [[PTR]].add - -; DIS-NEXT: i32.const 0 -; DIS-NEXT: i32.const 13 -; DIS-NEXT: memory.init 1, 0 - -; NOPIC-DIS-NEXT: [[PTR]].const 1044 -; PIC-DIS-NEXT: [[PTR]].const 20 -; PIC-DIS-NEXT: global.get 1 -; PIC-DIS-NEXT: [[PTR]].add - -; DIS-NEXT: i32.const 0 -; DIS-NEXT: i32.const 20 -; DIS-NEXT: memory.init 2, 0 -; NOPIC-DIS-NEXT: [[PTR]].const 1064 -; PIC-DIS-NEXT: [[PTR]].const 40 -; PIC-DIS-NEXT: global.get 1 -; PIC-DIS-NEXT: [[PTR]].add -; DIS-NEXT: i32.const 0 -; DIS-NEXT: i32.const 10000 -; DIS-NEXT: memory.fill 0 - -; NOPIC-DIS-NEXT: [[PTR]].const 11064 -; PIC-DIS-NEXT: local.get 0 - -; DIS-NEXT: i32.const 2 -; DIS-NEXT: i32.atomic.store 0 - -; NOPIC-DIS-NEXT: [[PTR]].const 11064 -; PIC-DIS-NEXT: local.get 0 - -; DIS-NEXT: i32.const -1 -; DIS-NEXT: memory.atomic.notify 0 -; DIS-NEXT: drop -; DIS-NEXT: br 1 # 1: down to label1 -; DIS-NEXT: end - -; NOPIC-DIS-NEXT: [[PTR]].const 11064 -; PIC-DIS-NEXT: local.get 0 - -; DIS-NEXT: i32.const 1 -; DIS-NEXT: i64.const -1 -; DIS-NEXT: memory.atomic.wait32 0 -; DIS-NEXT: drop -; DIS-NEXT: end -; DIS-NEXT: data.drop 1 -; DIS-NEXT: data.drop 2 -; DIS-NEXT: end +; RUN: llc --mtriple=wasm32-unknown-unknown -filetype=obj %s -o %t.atomics.o -mattr=+atomics +; RUN: llc --mtriple=wasm32-unknown-unknown -filetype=obj %s -o %t.bulk-mem.o -mattr=+bulk-memory +; RUN: llc --mtriple=wasm64-unknown-unknown -filetype=obj %s -o %t.bulk-mem64.o -mattr=+bulk-memory +; RUN: llc --mtriple=wasm32-unknown-unknown -filetype=obj %s -o %t.atomics.bulk-mem.o -mattr=+atomics,+bulk-memory +; RUN: llc --mtriple=wasm64-unknown-unknown -filetype=obj %s -o %t.atomics.bulk-mem64.o -mattr=+atomics,+bulk-memory +; RUN: llc --mtriple=wasm32-unknown-unknown -filetype=obj %s -o %t.atomics.bulk-mem.pic.o -relocation-model=pic -mattr=+atomics,+bulk-memory,+mutable-globals +; RUN: llc --mtriple=wasm64-unknown-unknown -filetype=obj %s -o %t.atomics.bulk-mem.pic-mem64.o -relocation-model=pic -mattr=+atomics,+bulk-memory,+mutable-globals + +; atomics, shared memory => error +; RUN: not wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.o -o %t.atomics.wasm 2>&1 | FileCheck %s --check-prefix ERROR + +; bulk memory, unshared memory => active segments +; RUN: wasm-ld -no-gc-sections --no-entry %t.bulk-mem.o -o %t.bulk-mem.wasm +; RUN: obj2yaml %t.bulk-mem.wasm | FileCheck %s --check-prefixes ACTIVE,ACTIVE32 + +; bulk memory, unshared memory, wasm64 => active segments +; RUN: wasm-ld -mwasm64 -no-gc-sections --no-entry %t.bulk-mem64.o -o %t.bulk-mem64.wasm +; RUN: obj2yaml %t.bulk-mem64.wasm | FileCheck %s --check-prefixes ACTIVE,ACTIVE64 + +; atomics, bulk memory, shared memory => passive segments +; RUN: wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem.o -o %t.atomics.bulk-mem.wasm +; RUN: obj2yaml %t.atomics.bulk-mem.wasm | FileCheck %s --check-prefix PASSIVE +; RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_init_memory --no-show-raw-insn --no-leading-addr %t.atomics.bulk-mem.wasm | FileCheck %s --check-prefixes DIS,NOPIC-DIS -DPTR=i32 + +; atomics, bulk memory, shared memory, wasm64 => passive segments +; RUN: wasm-ld -mwasm64 -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem64.o -o %t.atomics.bulk-mem64.wasm +; RUN: obj2yaml %t.atomics.bulk-mem64.wasm | FileCheck %s --check-prefix PASSIVE +; RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_init_memory --no-show-raw-insn --no-leading-addr %t.atomics.bulk-mem64.wasm | FileCheck %s --check-prefixes DIS,NOPIC-DIS -DPTR=i64 + +; Also test in combination with PIC/pie +; RUN: wasm-ld --experimental-pic -pie -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem.pic.o -o %t.pic.wasm +; RUN: obj2yaml %t.pic.wasm | FileCheck %s --check-prefixes PASSIVE-PIC,PASSIVE32-PIC +; RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs,__wasm_apply_data_relocs_tail,__wasm_init_memory --no-show-raw-insn --no-leading-addr %t.pic.wasm | FileCheck %s --check-prefixes DIS,PIC-DIS -DPTR=i32 + +; Also test in combination with PIC/pie + wasm64 +; RUN: wasm-ld -mwasm64 --experimental-pic -pie -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem.pic-mem64.o -o %t.pic-mem64.wasm +; RUN: obj2yaml %t.pic-mem64.wasm | FileCheck %s --check-prefixes PASSIVE-PIC,PASSIVE64-PIC +; RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs,__wasm_apply_data_relocs_tail,__wasm_init_memory --no-show-raw-insn --no-leading-addr %t.pic-mem64.wasm | FileCheck %s --check-prefixes DIS,PIC-DIS -DPTR=i64 + +@a = hidden global [6 x i8] c"hello\00", align 1 +@b = hidden global [8 x i8] c"goodbye\00", align 1 +@c = hidden global [10000 x i8] zeroinitializer, align 1 +@d = hidden global i32 42, align 4 + +@e = private constant [9 x i8] c"constant\00", align 1 +@f = private constant i8 43, align 4 + +@g = thread_local global i32 99, align 4 + +; ERROR: 'bulk-memory' feature must be used in order to use shared memory + +; ACTIVE-LABEL: - Type: CODE +; ACTIVE-NEXT: Functions: +; ACTIVE-NEXT: - Index: 0 +; ACTIVE-NEXT: Locals: [] +; ACTIVE-NEXT: Body: 0B +; ACTIVE-NEXT: - Type: DATA +; ACTIVE-NEXT: Segments: +; ACTIVE-NEXT: - SectionOffset: 7 +; ACTIVE-NEXT: InitFlags: 0 +; ACTIVE-NEXT: Offset: +; ACTIVE32-NEXT: Opcode: I32_CONST +; ACTIVE64-NEXT: Opcode: I64_CONST +; ACTIVE-NEXT: Value: 1024 +; ACTIVE-NEXT: Content: 636F6E7374616E74000000002B +; ACTIVE-NEXT: - SectionOffset: 26 +; ACTIVE-NEXT: InitFlags: 0 +; ACTIVE-NEXT: Offset: +; ACTIVE32-NEXT: Opcode: I32_CONST +; ACTIVE64-NEXT: Opcode: I64_CONST +; ACTIVE-NEXT: Value: 1040 +; ACTIVE-NEXT: Content: 68656C6C6F00676F6F646279650000002A000000 +; ACTIVE-NEXT: - Type: CUSTOM +; ACTIVE-NEXT: Name: name +; ACTIVE-NEXT: FunctionNames: +; ACTIVE-NEXT: - Index: 0 +; ACTIVE-NEXT: Name: __wasm_call_ctors + +; PASSIVE-LABEL: - Type: START +; PASSIVE-NEXT: StartFunction: 2 +; PASSIVE-LABEL: - Type: DATACOUNT +; PASSIVE-NEXT: Count: 3 +; PASSIVE-LABEL: - Type: CODE +; PASSIVE-NEXT: Functions: +; PASSIVE-NEXT: - Index: 0 +; PASSIVE-NEXT: Locals: [] +; PASSIVE-NEXT: Body: 0B +; PASSIVE-NEXT: - Index: 1 +; PASSIVE-NEXT: Locals: [] +; PASSIVE-NEXT: Body: {{.*}} +; PASSIVE-NEXT: - Index: 2 +; PASSIVE-NEXT: Locals: [] +; PASSIVE-NEXT: Body: {{.*}} +; PASSIVE-NEXT: - Type: DATA +; PASSIVE-NEXT: Segments: +; PASSIVE-NEXT: - SectionOffset: 3 +; PASSIVE-NEXT: InitFlags: 1 +; PASSIVE-NEXT: Content: '63000000' +; PASSIVE-NEXT: - SectionOffset: 9 +; PASSIVE-NEXT: InitFlags: 1 +; PASSIVE-NEXT: Content: 636F6E7374616E74000000002B +; PASSIVE-NEXT: - SectionOffset: 24 +; PASSIVE-NEXT: InitFlags: 1 +; PASSIVE-NEXT: Content: 68656C6C6F00676F6F646279650000002A000000 +; PASSIVE-NEXT: - Type: CUSTOM +; PASSIVE-NEXT: Name: name +; PASSIVE-NEXT: FunctionNames: +; PASSIVE-NEXT: - Index: 0 +; PASSIVE-NEXT: Name: __wasm_call_ctors +; PASSIVE-NEXT: - Index: 1 +; PASSIVE-NEXT: Name: __wasm_init_tls +; PASSIVE-NEXT: - Index: 2 +; PASSIVE-NEXT: Name: __wasm_init_memory + +; PASSIVE-PIC: - Type: START +; PASSIVE-PIC-NEXT: StartFunction: 4 +; PASSIVE-PIC-NEXT: - Type: DATACOUNT +; PASSIVE-PIC-NEXT: Count: 3 +; PASSIVE-PIC-NEXT: - Type: CODE +; PASSIVE-PIC-NEXT: Functions: +; PASSIVE-PIC-NEXT: - Index: 0 +; PASSIVE-PIC-NEXT: Locals: [] +; PASSIVE-PIC-NEXT: Body: 0B +; PASSIVE-PIC-NEXT: - Index: 1 +; PASSIVE-PIC-NEXT: Locals: [] +; PASSIVE-PIC-NEXT: Body: {{.*}} +; PASSIVE-PIC-NEXT: - Index: 2 +; PASSIVE-PIC-NEXT: Locals: [] +; PASSIVE-PIC-NEXT: Body: 0B +; PASSIVE-PIC-NEXT: - Index: 3 +; PASSIVE-PIC-NEXT: Locals: [] +; PASSIVE-PIC-NEXT: Body: 0B +; PASSIVE-PIC-NEXT: - Index: 4 +; PASSIVE-PIC-NEXT: Locals: +; PASSIVE32-PIC-NEXT: - Type: I32 +; PASSIVE64-PIC-NEXT: - Type: I64 +; PASSIVE-PIC-NEXT: Count: 2 +; PASSIVE-PIC-NEXT: Body: {{.*}} +; PASSIVE-PIC-NEXT: - Type: DATA +; PASSIVE-PIC-NEXT: Segments: +; PASSIVE-PIC-NEXT: - SectionOffset: 3 +; PASSIVE-PIC-NEXT: InitFlags: 1 +; PASSIVE-PIC-NEXT: Content: '63000000' +; PASSIVE-PIC-NEXT: - SectionOffset: 9 +; PASSIVE-PIC-NEXT: InitFlags: 1 +; PASSIVE-PIC-NEXT: Content: 636F6E7374616E74000000002B +; PASSIVE-PIC-NEXT: - SectionOffset: 24 +; PASSIVE-PIC-NEXT: InitFlags: 1 +; PASSIVE-PIC-NEXT: Content: 68656C6C6F00676F6F646279650000002A000000 +; PASSIVE-PIC-NEXT: - Type: CUSTOM +; PASSIVE-PIC-NEXT: Name: name +; PASSIVE-PIC-NEXT: FunctionNames: +; PASSIVE-PIC-NEXT: - Index: 0 +; PASSIVE-PIC-NEXT: Name: __wasm_call_ctors +; PASSIVE-PIC-NEXT: - Index: 1 +; PASSIVE-PIC-NEXT: Name: __wasm_init_tls +; PASSIVE-PIC-NEXT: - Index: 2 +; PASSIVE-PIC-NEXT: Name: __wasm_apply_data_relocs +; PASSIVE-PIC-NEXT: - Index: 3 +; PASSIVE-PIC-NEXT: Name: __wasm_apply_data_relocs_tail +; PASSIVE-PIC-NEXT: - Index: 4 +; PASSIVE-PIC-NEXT: Name: __wasm_init_memory + +; no data relocations. +; DIS-LABEL: <__wasm_call_ctors>: +; DIS-EMPTY: +; DIS-NEXT: end + +; In PIC mode __wasm_apply_data_relocs and __wasm_apply_data_relocs_tail +; are exported seperately to __wasm_call_ctors +; PIC-DIS: <__wasm_apply_data_relocs>: +; PIC-DIS-EMPTY: + +; PIC-DIS: <__wasm_apply_data_relocs_tail>: +; PIC-DIS-EMPTY: + +; DIS-LABEL: <__wasm_init_memory>: + +; PIC-DIS: .local [[PTR]] +; PIC-DIS-NEXT: global.get 1 +; PIC-DIS-NEXT: [[PTR]].const 10040 +; PIC-DIS-NEXT: [[PTR]].add +; PIC-DIS-NEXT: local.set 0 + +; DIS: block +; DIS-NEXT: block +; DIS-NEXT: block + +; NOPIC-DIS-NEXT: [[PTR]].const 11064 +; PIC-DIS-NEXT: local.get 0 + +; DIS-NEXT: i32.const 0 +; DIS-NEXT: i32.const 1 +; DIS-NEXT: i32.atomic.rmw.cmpxchg 0 +; DIS-NEXT: br_table {0, 1, 2} # 1: down to label1 +; DIS-NEXT: # 2: down to label0 +; DIS-NEXT: end + +; NOPIC-DIS-NEXT: [[PTR]].const 1024 +; NOPIC-DIS-NEXT: [[PTR]].const 1024 +; NOPIC-DIS-NEXT: global.set 1 +; PIC-DIS-NEXT: [[PTR]].const 0 +; PIC-DIS-NEXT: global.get 1 +; PIC-DIS-NEXT: [[PTR]].add +; PIC-DIS-NEXT: local.tee 1 +; PIC-DIS-NEXT: global.set {{\d*}} +; PIC-DIS-NEXT: local.get 1 +; DIS-NEXT: i32.const 0 +; DIS-NEXT: i32.const 4 +; DIS-NEXT: memory.init 0, 0 + +; NOPIC-DIS-NEXT: [[PTR]].const 1028 +; PIC-DIS-NEXT: [[PTR]].const 4 +; PIC-DIS-NEXT: global.get 1 +; PIC-DIS-NEXT: [[PTR]].add + +; DIS-NEXT: i32.const 0 +; DIS-NEXT: i32.const 13 +; DIS-NEXT: memory.init 1, 0 + +; NOPIC-DIS-NEXT: [[PTR]].const 1044 +; PIC-DIS-NEXT: [[PTR]].const 20 +; PIC-DIS-NEXT: global.get 1 +; PIC-DIS-NEXT: [[PTR]].add + +; DIS-NEXT: i32.const 0 +; DIS-NEXT: i32.const 20 +; DIS-NEXT: memory.init 2, 0 +; NOPIC-DIS-NEXT: [[PTR]].const 1064 +; PIC-DIS-NEXT: [[PTR]].const 40 +; PIC-DIS-NEXT: global.get 1 +; PIC-DIS-NEXT: [[PTR]].add +; DIS-NEXT: i32.const 0 +; DIS-NEXT: i32.const 10000 +; DIS-NEXT: memory.fill 0 + +; NOPIC-DIS-NEXT: [[PTR]].const 11064 +; PIC-DIS-NEXT: local.get 0 + +; DIS-NEXT: i32.const 2 +; DIS-NEXT: i32.atomic.store 0 + +; NOPIC-DIS-NEXT: [[PTR]].const 11064 +; PIC-DIS-NEXT: local.get 0 + +; DIS-NEXT: i32.const -1 +; DIS-NEXT: memory.atomic.notify 0 +; DIS-NEXT: drop +; DIS-NEXT: br 1 # 1: down to label1 +; DIS-NEXT: end + +; NOPIC-DIS-NEXT: [[PTR]].const 11064 +; PIC-DIS-NEXT: local.get 0 + +; DIS-NEXT: i32.const 1 +; DIS-NEXT: i64.const -1 +; DIS-NEXT: memory.atomic.wait32 0 +; DIS-NEXT: drop +; DIS-NEXT: end +; DIS-NEXT: data.drop 1 +; DIS-NEXT: data.drop 2 +; DIS-NEXT: end diff --git a/lld/test/wasm/pie.ll b/lld/test/wasm/pie.ll --- a/lld/test/wasm/pie.ll +++ b/lld/test/wasm/pie.ll @@ -1,184 +1,193 @@ -; RUN: llc -relocation-model=pic -mattr=+mutable-globals -filetype=obj %s -o %t.o -; RUN: wasm-ld --no-gc-sections --experimental-pic -pie -o %t.wasm %t.o -; RUN: obj2yaml %t.wasm | FileCheck %s -; RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DISASSEM - -target triple = "wasm32-unknown-emscripten" - -@data = global i32 2, align 4 -@data_external = external global i32 -@indirect_func = local_unnamed_addr global ptr @foo, align 4 - -@data_addr = local_unnamed_addr global ptr @data, align 4 -@data_addr_external = local_unnamed_addr global ptr @data_external, align 4 - -define hidden i32 @foo() { -entry: - ; To ensure we use __stack_pointer - %ptr = alloca i32 - %0 = load i32, ptr @data, align 4 - %1 = load ptr, ptr @indirect_func, align 4 - call i32 %1() - ret i32 %0 -} - -define default ptr @get_data_address() { -entry: - ret ptr @data_addr_external -} - -define void @_start() { - call void @external_func() - ret void -} - -declare void @external_func() - -; CHECK: Sections: -; CHECK-NEXT: - Type: CUSTOM -; CHECK-NEXT: Name: dylink.0 -; CHECK-NEXT: MemorySize: 16 -; CHECK-NEXT: MemoryAlignment: 2 -; CHECK-NEXT: TableSize: 1 -; CHECK-NEXT: TableAlignment: 0 -; CHECK-NEXT: Needed: [] - -; CHECK: - Type: IMPORT -; CHECK-NEXT: Imports: -; CHECK-NEXT: - Module: env -; CHECK-NEXT: Field: __indirect_function_table -; CHECK-NEXT: Kind: TABLE -; CHECK-NEXT: Table: -; CHECK-NEXT: Index: 0 -; CHECK-NEXT: ElemType: FUNCREF -; CHECK-NEXT: Limits: -; CHECK-NEXT: Minimum: 0x1 -; CHECK-NEXT: - Module: env -; CHECK-NEXT: Field: __stack_pointer -; CHECK-NEXT: Kind: GLOBAL -; CHECK-NEXT: GlobalType: I32 -; CHECK-NEXT: GlobalMutable: true -; CHECK-NEXT: - Module: env -; CHECK-NEXT: Field: __memory_base -; CHECK-NEXT: Kind: GLOBAL -; CHECK-NEXT: GlobalType: I32 -; CHECK-NEXT: GlobalMutable: false -; CHECK-NEXT: - Module: env -; CHECK-NEXT: Field: __table_base -; CHECK-NEXT: Kind: GLOBAL -; CHECK-NEXT: GlobalType: I32 -; CHECK-NEXT: GlobalMutable: false - -; CHECK: - Type: START -; CHECK-NEXT: StartFunction: 3 - -; CHECK: - Type: CUSTOM -; CHECK-NEXT: Name: name -; CHECK-NEXT: FunctionNames: -; CHECK-NEXT: - Index: 0 -; CHECK-NEXT: Name: external_func -; CHECK-NEXT: - Index: 1 -; CHECK-NEXT: Name: __wasm_call_ctors -; CHECK-NEXT: - Index: 2 -; CHECK-NEXT: Name: __wasm_apply_data_relocs -; CHECK-NEXT: - Index: 3 -; CHECK-NEXT: Name: __wasm_apply_global_relocs -; CHECK-NEXT: - Index: 4 -; CHECK-NEXT: Name: foo -; CHECK-NEXT: - Index: 5 -; CHECK-NEXT: Name: get_data_address -; CHECK-NEXT: - Index: 6 -; CHECK-NEXT: Name: _start -; CHECK-NEXT: GlobalNames: - -; DISASSEM-LABEL: <__wasm_call_ctors>: -; DISASSEM-EMPTY: -; DISASSEM-NEXT: end - -; DISASSEM-LABEL: <__wasm_apply_data_relocs>: -; DISASSEM: end - -; Run the same test with extended-const support. When this is available -; we don't need __wasm_apply_global_relocs and instead rely on the add -; instruction in the InitExpr. We also, therefore, do not need these globals -; to be mutable. - -; RUN: llc -relocation-model=pic -mattr=+extended-const,+mutable-globals,+atomics,+bulk-memory -filetype=obj %s -o %t.extended.o -; RUN: wasm-ld --no-gc-sections --allow-undefined --experimental-pic -pie -o %t.extended.wasm %t.extended.o -; RUN: obj2yaml %t.extended.wasm | FileCheck %s --check-prefix=EXTENDED-CONST - -; EXTENDED-CONST-NOT: __wasm_apply_global_relocs - -; EXTENDED-CONST: - Type: GLOBAL -; EXTENDED-CONST-NEXT: Globals: -; EXTENDED-CONST-NEXT: - Index: 4 -; EXTENDED-CONST-NEXT: Type: I32 -; EXTENDED-CONST-NEXT: Mutable: false -; EXTENDED-CONST-NEXT: InitExpr: -; EXTENDED-CONST-NEXT: Opcode: GLOBAL_GET -; EXTENDED-CONST-NEXT: Index: 1 -; EXTENDED-CONST-NEXT: - Index: 5 -; EXTENDED-CONST-NEXT: Type: I32 -; EXTENDED-CONST-NEXT: Mutable: false -; EXTENDED-CONST-NEXT: InitExpr: -; EXTENDED-CONST-NEXT: Extended: true -; EXTENDED-CONST-NEXT: Body: 230141046A0B -; EXTENDED-CONST-NEXT: - Index: 6 -; EXTENDED-CONST-NEXT: Type: I32 -; EXTENDED-CONST-NEXT: Mutable: false -; EXTENDED-CONST-NEXT: InitExpr: -; EXTENDED-CONST-NEXT: Extended: true -; This instruction sequence decodes to: -; (global.get[0x23] 0x1 i32.const[0x41] 0x0C i32.add[0x6A] end[0x0b]) -; EXTENDED-CONST-NEXT: Body: 2301410C6A0B - -; EXTENDED-CONST-NOT: - Type: START - -; EXTENDED-CONST: FunctionNames: -; EXTENDED-CONST-NEXT: - Index: 0 -; EXTENDED-CONST-NEXT: Name: external_func -; EXTENDED-CONST-NEXT: - Index: 1 -; EXTENDED-CONST-NEXT: Name: __wasm_call_ctors -; EXTENDED-CONST-NEXT: - Index: 2 -; EXTENDED-CONST-NEXT: Name: __wasm_apply_data_relocs - -; Run the same test with threading support. In this mode -; we expect __wasm_init_memory and __wasm_apply_data_relocs -; to be generated along with __wasm_start as the start -; function. - -; RUN: llc -relocation-model=pic -mattr=+mutable-globals,+atomics,+bulk-memory -filetype=obj %s -o %t.shmem.o -; RUN: wasm-ld --no-gc-sections --shared-memory --allow-undefined --experimental-pic -pie -o %t.shmem.wasm %t.shmem.o -; RUN: obj2yaml %t.shmem.wasm | FileCheck %s --check-prefix=SHMEM -; RUN: llvm-objdump --disassemble-symbols=__wasm_start --no-show-raw-insn --no-leading-addr %t.shmem.wasm | FileCheck %s --check-prefix DISASSEM-SHMEM - -; SHMEM: - Type: START -; SHMEM-NEXT: StartFunction: 6 - -; DISASSEM-SHMEM-LABEL: <__wasm_start>: -; DISASSEM-SHMEM-EMPTY: -; DISASSEM-SHMEM-NEXT: call 5 -; DISASSEM-SHMEM-NEXT: call 4 -; DISASSEM-SHMEM-NEXT: end - -; SHMEM: FunctionNames: -; SHMEM-NEXT: - Index: 0 -; SHMEM-NEXT: Name: external_func -; SHMEM-NEXT: - Index: 1 -; SHMEM-NEXT: Name: __wasm_call_ctors -; SHMEM-NEXT: - Index: 2 -; SHMEM-NEXT: Name: __wasm_init_tls -; SHMEM-NEXT: - Index: 3 -; SHMEM-NEXT: Name: __wasm_apply_data_relocs -; SHMEM-NEXT: - Index: 4 -; SHMEM-NEXT: Name: __wasm_init_memory -; SHMEM-NEXT: - Index: 5 -; SHMEM-NEXT: Name: __wasm_apply_global_relocs -; SHMEM-NEXT: - Index: 6 -; SHMEM-NEXT: Name: __wasm_start -; SHMEM-NEXT: - Index: 7 -; SHMEM-NEXT: Name: foo -; SHMEM-NEXT: - Index: 8 -; SHMEM-NEXT: Name: get_data_address -; SHMEM-NEXT: - Index: 9 -; SHMEM-NEXT: Name: _start +; RUN: llc -relocation-model=pic -mattr=+mutable-globals -filetype=obj %s -o %t.o +; RUN: wasm-ld --no-gc-sections --experimental-pic -pie -o %t.wasm %t.o +; RUN: obj2yaml %t.wasm | FileCheck %s +; RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs,__wasm_apply_data_relocs_tail --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DISASSEM + +target triple = "wasm32-unknown-emscripten" + +@data = global i32 2, align 4 +@data_external = external global i32 +@indirect_func = local_unnamed_addr global ptr @foo, align 4 + +@data_addr = local_unnamed_addr global ptr @data, align 4 +@data_addr_external = local_unnamed_addr global ptr @data_external, align 4 + +define hidden i32 @foo() { +entry: + ; To ensure we use __stack_pointer + %ptr = alloca i32 + %0 = load i32, ptr @data, align 4 + %1 = load ptr, ptr @indirect_func, align 4 + call i32 %1() + ret i32 %0 +} + +define default ptr @get_data_address() { +entry: + ret ptr @data_addr_external +} + +define void @_start() { + call void @external_func() + ret void +} + +declare void @external_func() + +; CHECK: Sections: +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: dylink.0 +; CHECK-NEXT: MemorySize: 16 +; CHECK-NEXT: MemoryAlignment: 2 +; CHECK-NEXT: TableSize: 1 +; CHECK-NEXT: TableAlignment: 0 +; CHECK-NEXT: Needed: [] + +; CHECK: - Type: IMPORT +; CHECK-NEXT: Imports: +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: __indirect_function_table +; CHECK-NEXT: Kind: TABLE +; CHECK-NEXT: Table: +; CHECK-NEXT: Index: 0 +; CHECK-NEXT: ElemType: FUNCREF +; CHECK-NEXT: Limits: +; CHECK-NEXT: Minimum: 0x1 +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: __stack_pointer +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: GlobalType: I32 +; CHECK-NEXT: GlobalMutable: true +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: __memory_base +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: GlobalType: I32 +; CHECK-NEXT: GlobalMutable: false +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: __table_base +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: GlobalType: I32 +; CHECK-NEXT: GlobalMutable: false + +; CHECK: - Type: START +; CHECK-NEXT: StartFunction: 4 + +; CHECK: - Type: CUSTOM +; CHECK-NEXT: Name: name +; CHECK-NEXT: FunctionNames: +; CHECK-NEXT: - Index: 0 +; CHECK-NEXT: Name: external_func +; CHECK-NEXT: - Index: 1 +; CHECK-NEXT: Name: __wasm_call_ctors +; CHECK-NEXT: - Index: 2 +; CHECK-NEXT: Name: __wasm_apply_data_relocs +; CHECK-NEXT: - Index: 3 +; CHECK-NEXT: Name: __wasm_apply_data_relocs_tail +; CHECK-NEXT: - Index: 4 +; CHECK-NEXT: Name: __wasm_apply_global_relocs +; CHECK-NEXT: - Index: 5 +; CHECK-NEXT: Name: foo +; CHECK-NEXT: - Index: 6 +; CHECK-NEXT: Name: get_data_address +; CHECK-NEXT: - Index: 7 +; CHECK-NEXT: Name: _start +; CHECK-NEXT: GlobalNames: + +; DISASSEM-LABEL: <__wasm_call_ctors>: +; DISASSEM-EMPTY: +; DISASSEM-NEXT: end + +; DISASSEM-LABEL: <__wasm_apply_data_relocs>: +; DISASSEM: end + +; DISASSEM-LABEL: <__wasm_apply_data_relocs_tail>: +; DISASSEM: end + +; Run the same test with extended-const support. When this is available +; we don't need __wasm_apply_global_relocs and instead rely on the add +; instruction in the InitExpr. We also, therefore, do not need these globals +; to be mutable. + +; RUN: llc -relocation-model=pic -mattr=+extended-const,+mutable-globals,+atomics,+bulk-memory -filetype=obj %s -o %t.extended.o +; RUN: wasm-ld --no-gc-sections --allow-undefined --experimental-pic -pie -o %t.extended.wasm %t.extended.o +; RUN: obj2yaml %t.extended.wasm | FileCheck %s --check-prefix=EXTENDED-CONST + +; EXTENDED-CONST-NOT: __wasm_apply_global_relocs + +; EXTENDED-CONST: - Type: GLOBAL +; EXTENDED-CONST-NEXT: Globals: +; EXTENDED-CONST-NEXT: - Index: 4 +; EXTENDED-CONST-NEXT: Type: I32 +; EXTENDED-CONST-NEXT: Mutable: false +; EXTENDED-CONST-NEXT: InitExpr: +; EXTENDED-CONST-NEXT: Opcode: GLOBAL_GET +; EXTENDED-CONST-NEXT: Index: 1 +; EXTENDED-CONST-NEXT: - Index: 5 +; EXTENDED-CONST-NEXT: Type: I32 +; EXTENDED-CONST-NEXT: Mutable: false +; EXTENDED-CONST-NEXT: InitExpr: +; EXTENDED-CONST-NEXT: Extended: true +; EXTENDED-CONST-NEXT: Body: 230141046A0B +; EXTENDED-CONST-NEXT: - Index: 6 +; EXTENDED-CONST-NEXT: Type: I32 +; EXTENDED-CONST-NEXT: Mutable: false +; EXTENDED-CONST-NEXT: InitExpr: +; EXTENDED-CONST-NEXT: Extended: true +; This instruction sequence decodes to: +; (global.get[0x23] 0x1 i32.const[0x41] 0x0C i32.add[0x6A] end[0x0b]) +; EXTENDED-CONST-NEXT: Body: 2301410C6A0B + +; EXTENDED-CONST-NOT: - Type: START + +; EXTENDED-CONST: FunctionNames: +; EXTENDED-CONST-NEXT: - Index: 0 +; EXTENDED-CONST-NEXT: Name: external_func +; EXTENDED-CONST-NEXT: - Index: 1 +; EXTENDED-CONST-NEXT: Name: __wasm_call_ctors +; EXTENDED-CONST-NEXT: - Index: 2 +; EXTENDED-CONST-NEXT: Name: __wasm_apply_data_relocs +; EXTENDED-CONST-NEXT: - Index: 3 +; EXTENDED-CONST-NEXT: Name: __wasm_apply_data_relocs_tail + +; Run the same test with threading support. In this mode we expect +; __wasm_init_memory, __wasm_apply_data_relocs and __wasm_apply_data_relocs +; to be generated along with __wasm_start as the start +; function. + +; RUN: llc -relocation-model=pic -mattr=+mutable-globals,+atomics,+bulk-memory -filetype=obj %s -o %t.shmem.o +; RUN: wasm-ld --no-gc-sections --shared-memory --allow-undefined --experimental-pic -pie -o %t.shmem.wasm %t.shmem.o +; RUN: obj2yaml %t.shmem.wasm | FileCheck %s --check-prefix=SHMEM +; RUN: llvm-objdump --disassemble-symbols=__wasm_start --no-show-raw-insn --no-leading-addr %t.shmem.wasm | FileCheck %s --check-prefix DISASSEM-SHMEM + +; SHMEM: - Type: START +; SHMEM-NEXT: StartFunction: 7 + +; DISASSEM-SHMEM-LABEL: <__wasm_start>: +; DISASSEM-SHMEM-EMPTY: +; DISASSEM-SHMEM-NEXT: call 6 +; DISASSEM-SHMEM-NEXT: call 5 +; DISASSEM-SHMEM-NEXT: end + +; SHMEM: FunctionNames: +; SHMEM-NEXT: - Index: 0 +; SHMEM-NEXT: Name: external_func +; SHMEM-NEXT: - Index: 1 +; SHMEM-NEXT: Name: __wasm_call_ctors +; SHMEM-NEXT: - Index: 2 +; SHMEM-NEXT: Name: __wasm_init_tls +; SHMEM-NEXT: - Index: 3 +; SHMEM-NEXT: Name: __wasm_apply_data_relocs +; SHMEM-NEXT: - Index: 4 +; SHMEM-NEXT: Name: __wasm_apply_data_relocs_tail +; SHMEM-NEXT: - Index: 5 +; SHMEM-NEXT: Name: __wasm_init_memory +; SHMEM-NEXT: - Index: 6 +; SHMEM-NEXT: Name: __wasm_apply_global_relocs +; SHMEM-NEXT: - Index: 7 +; SHMEM-NEXT: Name: __wasm_start +; SHMEM-NEXT: - Index: 8 +; SHMEM-NEXT: Name: foo +; SHMEM-NEXT: - Index: 9 +; SHMEM-NEXT: Name: get_data_address +; SHMEM-NEXT: - Index: 10 +; SHMEM-NEXT: Name: _start diff --git a/lld/test/wasm/shared-weak-symbols.s b/lld/test/wasm/shared-weak-symbols.s --- a/lld/test/wasm/shared-weak-symbols.s +++ b/lld/test/wasm/shared-weak-symbols.s @@ -1,90 +1,95 @@ -# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s -# RUN: wasm-ld --experimental-pic -shared -o %t.wasm %t.o -# RUN: obj2yaml %t.wasm | FileCheck %s -# RUN: llvm-objdump -d %t.wasm | FileCheck %s -check-prefix=ASM - -# Verify the weakly defined fuctions (weak_func) are both imported and exported, -# and that internal usage (direct call) always uses the imported version. -# Hidden functions, even if weak, should not be imported or exported. - -.globl weak_func -.weak weak_func -weak_func: - .functype weak_func () -> (i32) - i32.const 0 - end_function - -.globl hidden_weak_func -.hidden hidden_weak_func -.weak hidden_weak_func -hidden_weak_func: - .functype hidden_weak_func () -> (i32) - i32.const 42 - end_function - -.globl call_weak -call_weak: -# ASM: : - .functype call_weak () -> (i32) - call weak_func -# ASM: 10 80 80 80 80 00 call 0 - drop - call hidden_weak_func -# ASM: 10 84 80 80 80 00 call 4 - end_function -# ASM-NEXT: 0b end - -# CHECK: - Type: IMPORT -# CHECK-NEXT: Imports: -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: memory -# CHECK-NEXT: Kind: MEMORY -# CHECK-NEXT: Memory: -# CHECK-NEXT: Minimum: 0x0 -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __memory_base -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: false -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __table_base -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: false -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: weak_func -# CHECK-NEXT: Kind: FUNCTION -# CHECK-NEXT: SigIndex: 0 -# CHECK-NEXT: - Type: FUNCTION - -# CHECK: - Type: EXPORT -# CHECK-NEXT: Exports: -# CHECK-NEXT: - Name: __wasm_call_ctors -# CHECK-NEXT: Kind: FUNCTION -# CHECK-NEXT: Index: 1 -# CHECK-NEXT: - Name: __wasm_apply_data_relocs -# CHECK-NEXT: Kind: FUNCTION -# CHECK-NEXT: Index: 2 -# CHECK-NEXT: - Name: weak_func -# CHECK-NEXT: Kind: FUNCTION -# CHECK-NEXT: Index: 3 -# CHECK-NEXT: - Name: call_weak -# CHECK-NEXT: Kind: FUNCTION -# CHECK-NEXT: Index: 5 -# CHECK-NEXT: - Type: CODE - -# CHECK: - Type: CUSTOM -# CHECK-NEXT: Name: name -# CHECK-NEXT: FunctionNames: -# CHECK-NEXT: - Index: 0 -# CHECK-NEXT: Name: weak_func -# CHECK-NEXT: - Index: 1 -# CHECK-NEXT: Name: __wasm_call_ctors -# CHECK-NEXT: - Index: 2 -# CHECK-NEXT: Name: __wasm_apply_data_relocs -# CHECK-NEXT: - Index: 3 -# CHECK-NEXT: Name: weak_func -# CHECK-NEXT: - Index: 4 -# CHECK-NEXT: Name: hidden_weak_func -# CHECK-NEXT: - Index: 5 -# CHECK-NEXT: Name: call_weak +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld --experimental-pic -shared -o %t.wasm %t.o +# RUN: obj2yaml %t.wasm | FileCheck %s +# RUN: llvm-objdump -d %t.wasm | FileCheck %s -check-prefix=ASM + +# Verify the weakly defined fuctions (weak_func) are both imported and exported, +# and that internal usage (direct call) always uses the imported version. +# Hidden functions, even if weak, should not be imported or exported. + +.globl weak_func +.weak weak_func +weak_func: + .functype weak_func () -> (i32) + i32.const 0 + end_function + +.globl hidden_weak_func +.hidden hidden_weak_func +.weak hidden_weak_func +hidden_weak_func: + .functype hidden_weak_func () -> (i32) + i32.const 42 + end_function + +.globl call_weak +call_weak: +# ASM: : + .functype call_weak () -> (i32) + call weak_func +# ASM: 10 80 80 80 80 00 call 0 + drop + call hidden_weak_func +# ASM: 10 85 80 80 80 00 call 5 + end_function +# ASM-NEXT: 0b end + +# CHECK: - Type: IMPORT +# CHECK-NEXT: Imports: +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: memory +# CHECK-NEXT: Kind: MEMORY +# CHECK-NEXT: Memory: +# CHECK-NEXT: Minimum: 0x0 +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __memory_base +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: false +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __table_base +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: false +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: weak_func +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: SigIndex: 0 +# CHECK-NEXT: - Type: FUNCTION + +# CHECK: - Type: EXPORT +# CHECK-NEXT: Exports: +# CHECK-NEXT: - Name: __wasm_call_ctors +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: - Name: __wasm_apply_data_relocs +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 2 +# CHECK-NEXT: - Name: __wasm_apply_data_relocs_tail +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 3 +# CHECK-NEXT: - Name: weak_func +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 4 +# CHECK-NEXT: - Name: call_weak +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 6 +# CHECK-NEXT: - Type: CODE + +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: name +# CHECK-NEXT: FunctionNames: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: Name: weak_func +# CHECK-NEXT: - Index: 1 +# CHECK-NEXT: Name: __wasm_call_ctors +# CHECK-NEXT: - Index: 2 +# CHECK-NEXT: Name: __wasm_apply_data_relocs +# CHECK-NEXT: - Index: 3 +# CHECK-NEXT: Name: __wasm_apply_data_relocs_tail +# CHECK-NEXT: - Index: 4 +# CHECK-NEXT: Name: weak_func +# CHECK-NEXT: - Index: 5 +# CHECK-NEXT: Name: hidden_weak_func +# CHECK-NEXT: - Index: 6 +# CHECK-NEXT: Name: call_weak diff --git a/lld/test/wasm/shared.s b/lld/test/wasm/shared.s --- a/lld/test/wasm/shared.s +++ b/lld/test/wasm/shared.s @@ -1,258 +1,263 @@ -# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s -# RUN: wasm-ld --experimental-pic -shared -o %t.wasm %t.o -# RUN: obj2yaml %t.wasm | FileCheck %s -# RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS - -.functype func_external () -> () - -# Linker-synthesized globals -.globaltype __stack_pointer, i32 -.globaltype __table_base, i32, immutable -.globaltype __memory_base, i32, immutable - -.section .data.data,"",@ -data: - .p2align 2 - .int32 2 - .size data, 4 - -.section .data.indirect_func_external,"",@ -indirect_func_external: - .int32 func_external -.size indirect_func_external, 4 - -.section .data.indirect_func,"",@ -indirect_func: - .int32 foo - .size indirect_func, 4 - -# Test data relocations - -.section .data.data_addr,"",@ -data_addr: - .int32 data - .size data_addr, 4 - -# .. against external symbols - -.section .data.data_addr_external,"",@ -data_addr_external: - .int32 data_external - .size data_addr_external, 4 - -# .. including addends - -.section .data.extern_struct_internal_ptr,"",@ -extern_struct_internal_ptr: - .int32 extern_struct + 4 - .size extern_struct_internal_ptr, 4 - -# Test use of __stack_pointer - -.section .text,"",@ -foo: - # %ptr = alloca i32 - # %0 = load i32, ptr @data, align 4 - # %1 = load ptr, ptr @indirect_func, align 4 - # call i32 %1() - # ret i32 %0 - .functype foo () -> (i32) - .local i32, i32 - global.get __stack_pointer - i32.const 16 - i32.sub - local.tee 0 - global.set __stack_pointer - global.get __memory_base - i32.const data@MBREL - i32.add - i32.load 0 - local.set 1 - global.get indirect_func@GOT - i32.load 0 - call_indirect () -> (i32) - drop - local.get 0 - i32.const 16 - i32.add - global.set __stack_pointer - local.get 1 - end_function - -get_func_address: - .functype get_func_address () -> (i32) - global.get func_external@GOT - end_function - -get_data_address: - .functype get_data_address () -> (i32) - global.get data_external@GOT - end_function - -get_local_func_address: - # Verify that a function which is otherwise not address taken *is* added to - # the wasm table with referenced via R_WASM_TABLE_INDEX_REL_SLEB - .functype get_local_func_address () -> (i32) - global.get __table_base - i32.const get_func_address@TBREL - i32.add - end_function - -.globl foo -.globl data -.globl indirect_func -.globl indirect_func_external -.globl data_addr -.globl data_addr_external -.globl extern_struct_internal_ptr -.globl get_data_address -.globl get_func_address -.globl get_local_func_address - -.hidden foo -.hidden data -.hidden get_data_address -.hidden get_func_address - -# Without this linking will fail because we import __stack_pointer (a mutable -# global). -# TODO(sbc): We probably want a nicer way to specify target_features section -# in assembly. -.section .custom_section.target_features,"",@ -.int8 1 -.int8 43 -.int8 15 -.ascii "mutable-globals" - -# check for dylink section at start - -# CHECK: Sections: -# CHECK-NEXT: - Type: CUSTOM -# CHECK-NEXT: Name: dylink.0 -# CHECK-NEXT: MemorySize: 24 -# CHECK-NEXT: MemoryAlignment: 2 -# CHECK-NEXT: TableSize: 2 -# CHECK-NEXT: TableAlignment: 0 -# CHECK-NEXT: Needed: [] -# CHECK-NEXT: - Type: TYPE - -# check for import of __table_base and __memory_base globals - -# CHECK: - Type: IMPORT -# CHECK-NEXT: Imports: -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: memory -# CHECK-NEXT: Kind: MEMORY -# CHECK-NEXT: Memory: -# CHECK-NEXT: Minimum: 0x1 -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __indirect_function_table -# CHECK-NEXT: Kind: TABLE -# CHECK-NEXT: Table: -# CHECK-NEXT: Index: 0 -# CHECK-NEXT: ElemType: FUNCREF -# CHECK-NEXT: Limits: -# CHECK-NEXT: Minimum: 0x2 -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __stack_pointer -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __memory_base -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: false -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __table_base -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: false -# CHECK-NEXT: - Module: GOT.mem -# CHECK-NEXT: Field: indirect_func -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Module: GOT.func -# CHECK-NEXT: Field: func_external -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Module: GOT.mem -# CHECK-NEXT: Field: data_external -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Module: GOT.mem -# CHECK-NEXT: Field: extern_struct -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Type: FUNCTION - -# CHECK: - Type: EXPORT -# CHECK-NEXT: Exports: -# CHECK-NEXT: - Name: __wasm_call_ctors -# CHECK-NEXT: Kind: FUNCTION -# CHECK-NEXT: Index: 0 - -# check for elem segment initialized with __table_base global as offset - -# CHECK: - Type: ELEM -# CHECK-NEXT: Segments: -# CHECK-NEXT: - Offset: -# CHECK-NEXT: Opcode: GLOBAL_GET -# CHECK-NEXT: Index: 2 -# CHECK-NEXT: Functions: [ 3, 2 ] - -# check the generated code in __wasm_call_ctors and __wasm_apply_data_relocs functions - -# DIS: <__wasm_call_ctors>: -# DIS-EMPTY: -# DIS-NEXT: end - -# DIS: <__wasm_apply_data_relocs>: -# DIS-EMPTY: -# DIS-NEXT: i32.const 4 -# DIS-NEXT: global.get 1 -# DIS-NEXT: i32.add -# DIS-NEXT: global.get 4 -# DIS-NEXT: i32.store 0 -# DIS-NEXT: i32.const 8 -# DIS-NEXT: global.get 1 -# DIS-NEXT: i32.add -# DIS-NEXT: global.get 2 -# DIS-NEXT: i32.const 1 -# DIS-NEXT: i32.add -# DIS-NEXT: i32.store 0 -# DIS-NEXT: i32.const 12 -# DIS-NEXT: global.get 1 -# DIS-NEXT: i32.add -# DIS-NEXT: global.get 1 -# DIS-NEXT: i32.const 0 -# DIS-NEXT: i32.add -# DIS-NEXT: i32.store 0 -# DIS-NEXT: i32.const 16 -# DIS-NEXT: global.get 1 -# DIS-NEXT: i32.add -# DIS-NEXT: global.get 5 -# DIS-NEXT: i32.store 0 -# DIS-NEXT: i32.const 20 -# DIS-NEXT: global.get 1 -# DIS-NEXT: i32.add -# DIS-NEXT: global.get 6 -# DIS-NEXT: i32.const 4 -# DIS-NEXT: i32.add -# DIS-NEXT: i32.store 0 -# DIS-NEXT: end - -# check the data segment initialized with __memory_base global as offset - -# CHECK: - Type: DATA -# CHECK-NEXT: Segments: -# CHECK-NEXT: - SectionOffset: 6 -# CHECK-NEXT: InitFlags: 0 -# CHECK-NEXT: Offset: -# CHECK-NEXT: Opcode: GLOBAL_GET -# CHECK-NEXT: Index: 1 -# CHECK-NEXT: Content: '020000000000000001000000000000000000000000000000' +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld --experimental-pic -shared -o %t.wasm %t.o +# RUN: obj2yaml %t.wasm | FileCheck %s +# RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs,__wasm_apply_data_relocs_tail --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS + +.functype func_external () -> () + +# Linker-synthesized globals +.globaltype __stack_pointer, i32 +.globaltype __table_base, i32, immutable +.globaltype __memory_base, i32, immutable + +.section .data.data,"",@ +data: + .p2align 2 + .int32 2 + .size data, 4 + +.section .data.indirect_func_external,"",@ +indirect_func_external: + .int32 func_external +.size indirect_func_external, 4 + +.section .data.indirect_func,"",@ +indirect_func: + .int32 foo + .size indirect_func, 4 + +# Test data relocations + +.section .data.data_addr,"",@ +data_addr: + .int32 data + .size data_addr, 4 + +# .. against external symbols + +.section .data.data_addr_external,"",@ +data_addr_external: + .int32 data_external + .size data_addr_external, 4 + +# .. including addends + +.section .data.extern_struct_internal_ptr,"",@ +extern_struct_internal_ptr: + .int32 extern_struct + 4 + .size extern_struct_internal_ptr, 4 + +# Test use of __stack_pointer + +.section .text,"",@ +foo: + # %ptr = alloca i32 + # %0 = load i32, ptr @data, align 4 + # %1 = load ptr, ptr @indirect_func, align 4 + # call i32 %1() + # ret i32 %0 + .functype foo () -> (i32) + .local i32, i32 + global.get __stack_pointer + i32.const 16 + i32.sub + local.tee 0 + global.set __stack_pointer + global.get __memory_base + i32.const data@MBREL + i32.add + i32.load 0 + local.set 1 + global.get indirect_func@GOT + i32.load 0 + call_indirect () -> (i32) + drop + local.get 0 + i32.const 16 + i32.add + global.set __stack_pointer + local.get 1 + end_function + +get_func_address: + .functype get_func_address () -> (i32) + global.get func_external@GOT + end_function + +get_data_address: + .functype get_data_address () -> (i32) + global.get data_external@GOT + end_function + +get_local_func_address: + # Verify that a function which is otherwise not address taken *is* added to + # the wasm table with referenced via R_WASM_TABLE_INDEX_REL_SLEB + .functype get_local_func_address () -> (i32) + global.get __table_base + i32.const get_func_address@TBREL + i32.add + end_function + +.globl foo +.globl data +.globl indirect_func +.globl indirect_func_external +.globl data_addr +.globl data_addr_external +.globl extern_struct_internal_ptr +.globl get_data_address +.globl get_func_address +.globl get_local_func_address + +.hidden foo +.hidden data +.hidden get_data_address +.hidden get_func_address + +# Without this linking will fail because we import __stack_pointer (a mutable +# global). +# TODO(sbc): We probably want a nicer way to specify target_features section +# in assembly. +.section .custom_section.target_features,"",@ +.int8 1 +.int8 43 +.int8 15 +.ascii "mutable-globals" + +# check for dylink section at start + +# CHECK: Sections: +# CHECK-NEXT: - Type: CUSTOM +# CHECK-NEXT: Name: dylink.0 +# CHECK-NEXT: MemorySize: 24 +# CHECK-NEXT: MemoryAlignment: 2 +# CHECK-NEXT: TableSize: 2 +# CHECK-NEXT: TableAlignment: 0 +# CHECK-NEXT: Needed: [] +# CHECK-NEXT: - Type: TYPE + +# check for import of __table_base and __memory_base globals + +# CHECK: - Type: IMPORT +# CHECK-NEXT: Imports: +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: memory +# CHECK-NEXT: Kind: MEMORY +# CHECK-NEXT: Memory: +# CHECK-NEXT: Minimum: 0x1 +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __indirect_function_table +# CHECK-NEXT: Kind: TABLE +# CHECK-NEXT: Table: +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: ElemType: FUNCREF +# CHECK-NEXT: Limits: +# CHECK-NEXT: Minimum: 0x2 +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __stack_pointer +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __memory_base +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: false +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __table_base +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: false +# CHECK-NEXT: - Module: GOT.mem +# CHECK-NEXT: Field: indirect_func +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Module: GOT.func +# CHECK-NEXT: Field: func_external +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Module: GOT.mem +# CHECK-NEXT: Field: data_external +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Module: GOT.mem +# CHECK-NEXT: Field: extern_struct +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Type: FUNCTION + +# CHECK: - Type: EXPORT +# CHECK-NEXT: Exports: +# CHECK-NEXT: - Name: __wasm_call_ctors +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 0 + +# check for elem segment initialized with __table_base global as offset + +# CHECK: - Type: ELEM +# CHECK-NEXT: Segments: +# CHECK-NEXT: - Offset: +# CHECK-NEXT: Opcode: GLOBAL_GET +# CHECK-NEXT: Index: 2 +# CHECK-NEXT: Functions: [ 4, 3 ] + +# check the generated code in __wasm_call_ctors, __wasm_apply_data_relocs +# and __wasm_apply_data_relocs_tail functions + +# DIS: <__wasm_call_ctors>: +# DIS-EMPTY: +# DIS-NEXT: end + +# DIS: <__wasm_apply_data_relocs>: +# DIS-EMPTY: +# DIS-NEXT: i32.const 4 +# DIS-NEXT: global.get 1 +# DIS-NEXT: i32.add +# DIS-NEXT: global.get 4 +# DIS-NEXT: i32.store 0 +# DIS-NEXT: i32.const 8 +# DIS-NEXT: global.get 1 +# DIS-NEXT: i32.add +# DIS-NEXT: global.get 2 +# DIS-NEXT: i32.const 1 +# DIS-NEXT: i32.add +# DIS-NEXT: i32.store 0 +# DIS-NEXT: i32.const 12 +# DIS-NEXT: global.get 1 +# DIS-NEXT: i32.add +# DIS-NEXT: global.get 1 +# DIS-NEXT: i32.const 0 +# DIS-NEXT: i32.add +# DIS-NEXT: i32.store 0 +# DIS-NEXT: i32.const 16 +# DIS-NEXT: global.get 1 +# DIS-NEXT: i32.add +# DIS-NEXT: global.get 5 +# DIS-NEXT: i32.store 0 +# DIS-NEXT: i32.const 20 +# DIS-NEXT: global.get 1 +# DIS-NEXT: i32.add +# DIS-NEXT: global.get 6 +# DIS-NEXT: i32.const 4 +# DIS-NEXT: i32.add +# DIS-NEXT: i32.store 0 +# DIS-NEXT: end + +# DIS: <__wasm_apply_data_relocs_tail>: +# DIS-EMPTY: +# DIS-NEXT: end + +# check the data segment initialized with __memory_base global as offset + +# CHECK: - Type: DATA +# CHECK-NEXT: Segments: +# CHECK-NEXT: - SectionOffset: 6 +# CHECK-NEXT: InitFlags: 0 +# CHECK-NEXT: Offset: +# CHECK-NEXT: Opcode: GLOBAL_GET +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: Content: '020000000000000001000000000000000000000000000000' diff --git a/lld/test/wasm/shared64.s b/lld/test/wasm/shared64.s --- a/lld/test/wasm/shared64.s +++ b/lld/test/wasm/shared64.s @@ -1,265 +1,270 @@ -# RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown -o %t.o %s -# RUN: wasm-ld -mwasm64 --experimental-pic -shared -o %t.wasm %t.o -# RUN: obj2yaml %t.wasm | FileCheck %s -# RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS - -.functype func_external () -> () - -# Linker-synthesized globals -.globaltype __stack_pointer, i64 -.globaltype __table_base, i64, immutable -.globaltype __memory_base, i64, immutable - -.section .data.data,"",@ -data: - .p2align 2 - .int32 2 - .size data, 4 - -.section .data.indirect_func_external,"",@ -indirect_func_external: - .int64 func_external -.size indirect_func_external, 8 - -.section .data.indirect_func,"",@ -indirect_func: - .int64 foo - .size indirect_func, 8 - -# Test data relocations - -.section .data.data_addr,"",@ -data_addr: - .int32 data - .size data_addr, 4 - -# .. against external symbols - -.section .data.data_addr_external,"",@ -data_addr_external: - .int64 data_external - .size data_addr_external, 8 - -# .. including addends - -.section .data.extern_struct_internal_ptr,"",@ -extern_struct_internal_ptr: - .int32 extern_struct + 4 - .size extern_struct_internal_ptr, 4 - -# Test use of __stack_pointer - -.section .text,"",@ -foo: - # %ptr = alloca i32 - # %0 = load i32, ptr @data, align 4 - # %1 = load ptr, ptr @indirect_func, align 4 - # call i32 %1() - # ret i32 %0 - .functype foo () -> (i32) - .local i64, i32 - global.get __stack_pointer - i64.const 16 - i64.sub - local.tee 0 - global.set __stack_pointer - global.get __memory_base - i64.const data@MBREL - i64.add - i32.load 0 - local.set 1 - global.get indirect_func@GOT - i64.load 0 - i32.wrap_i64 - call_indirect () -> (i32) - drop - local.get 0 - i64.const 16 - i64.add - global.set __stack_pointer - local.get 1 - end_function - -get_func_address: - .functype get_func_address () -> (i64) - global.get func_external@GOT - end_function - -get_data_address: - .functype get_data_address () -> (i64) - global.get data_external@GOT - end_function - -get_local_func_address: - # Verify that a function which is otherwise not address taken *is* added to - # the wasm table with referenced via R_WASM_TABLE_INDEX_REL_SLEB64 - .functype get_local_func_address () -> (i64) - global.get __table_base - i64.const get_func_address@TBREL - i64.add - end_function - -.globl foo -.globl data -.globl indirect_func -.globl indirect_func_external -.globl data_addr -.globl data_addr_external -.globl extern_struct_internal_ptr -.globl get_data_address -.globl get_func_address -.globl get_local_func_address - -.hidden foo -.hidden data -.hidden get_data_address -.hidden get_func_address - -# Without this linking will fail because we import __stack_pointer (a mutable -# global). -# TODO(sbc): We probably want a nicer way to specify target_features section -# in assembly. -.section .custom_section.target_features,"",@ -.int8 1 -.int8 43 -.int8 15 -.ascii "mutable-globals" - -# check for dylink section at start - -# CHECK: Sections: -# CHECK-NEXT: - Type: CUSTOM -# CHECK-NEXT: Name: dylink.0 -# CHECK-NEXT: MemorySize: 36 -# CHECK-NEXT: MemoryAlignment: 2 -# CHECK-NEXT: TableSize: 2 -# CHECK-NEXT: TableAlignment: 0 -# CHECK-NEXT: Needed: [] -# CHECK-NEXT: - Type: TYPE - -# check for import of __table_base and __memory_base globals - -# CHECK: - Type: IMPORT -# CHECK-NEXT: Imports: -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: memory -# CHECK-NEXT: Kind: MEMORY -# CHECK-NEXT: Memory: -# CHECK-NEXT: Flags: [ IS_64 ] -# CHECK-NEXT: Minimum: 0x1 -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __indirect_function_table -# CHECK-NEXT: Kind: TABLE -# CHECK-NEXT: Table: -# CHECK-NEXT: Index: 0 -# CHECK-NEXT: ElemType: FUNCREF -# CHECK-NEXT: Limits: -# CHECK-NEXT: Minimum: 0x2 -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __stack_pointer -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I64 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __memory_base -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I64 -# CHECK-NEXT: GlobalMutable: false -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __table_base -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I64 -# CHECK-NEXT: GlobalMutable: false -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: __table_base32 -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: false -# CHECK-NEXT: - Module: GOT.mem -# CHECK-NEXT: Field: indirect_func -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I64 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Module: GOT.func -# CHECK-NEXT: Field: func_external -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I64 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Module: GOT.mem -# CHECK-NEXT: Field: data_external -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I64 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Module: GOT.mem -# CHECK-NEXT: Field: extern_struct -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I64 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Type: FUNCTION - -# CHECK: - Type: EXPORT -# CHECK-NEXT: Exports: -# CHECK-NEXT: - Name: __wasm_call_ctors -# CHECK-NEXT: Kind: FUNCTION -# CHECK-NEXT: Index: 0 - -# check for elem segment initialized with __table_base global as offset - -# CHECK: - Type: ELEM -# CHECK-NEXT: Segments: -# CHECK-NEXT: - Offset: -# CHECK-NEXT: Opcode: GLOBAL_GET -# CHECK-NEXT: Index: 3 -# CHECK-NEXT: Functions: [ 3, 2 ] - -# check the generated code in __wasm_call_ctors and __wasm_apply_data_relocs functions - -# DIS: <__wasm_call_ctors>: -# DIS-EMPTY: -# DIS-NEXT: end - -# DIS: <__wasm_apply_data_relocs>: -# DIS-EMPTY: -# DIS-NEXT: i64.const 4 -# DIS-NEXT: global.get 1 -# DIS-NEXT: i64.add -# DIS-NEXT: global.get 5 -# DIS-NEXT: i64.store 0:p2align=2 -# DIS-NEXT: i64.const 12 -# DIS-NEXT: global.get 1 -# DIS-NEXT: i64.add -# DIS-NEXT: global.get 2 -# DIS-NEXT: i64.const 1 -# DIS-NEXT: i64.add -# DIS-NEXT: i64.store 0:p2align=2 -# DIS-NEXT: i64.const 20 -# DIS-NEXT: global.get 1 -# DIS-NEXT: i64.add -# DIS-NEXT: global.get 1 -# DIS-NEXT: i32.const 0 -# DIS-NEXT: i32.add -# DIS-NEXT: i32.store 0 -# DIS-NEXT: i64.const 24 -# DIS-NEXT: global.get 1 -# DIS-NEXT: i64.add -# DIS-NEXT: global.get 6 -# DIS-NEXT: i64.store 0:p2align=2 -# DIS-NEXT: i64.const 32 -# DIS-NEXT: global.get 1 -# DIS-NEXT: i64.add -# DIS-NEXT: global.get 7 -# DIS-NEXT: i32.const 4 -# DIS-NEXT: i32.add -# DIS-NEXT: i32.store 0 -# DIS-NEXT: end - -# check the data segment initialized with __memory_base global as offset - -# CHECK: - Type: DATA -# CHECK-NEXT: Segments: -# CHECK-NEXT: - SectionOffset: 6 -# CHECK-NEXT: InitFlags: 0 -# CHECK-NEXT: Offset: -# CHECK-NEXT: Opcode: GLOBAL_GET -# CHECK-NEXT: Index: 1 -# CHECK-NEXT: Content: '020000000000000000000000010000000000000000000000000000000000000000000000' +# RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown -o %t.o %s +# RUN: wasm-ld -mwasm64 --experimental-pic -shared -o %t.wasm %t.o +# RUN: obj2yaml %t.wasm | FileCheck %s +# RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs,__wasm_apply_data_relocs_tail --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS + +.functype func_external () -> () + +# Linker-synthesized globals +.globaltype __stack_pointer, i64 +.globaltype __table_base, i64, immutable +.globaltype __memory_base, i64, immutable + +.section .data.data,"",@ +data: + .p2align 2 + .int32 2 + .size data, 4 + +.section .data.indirect_func_external,"",@ +indirect_func_external: + .int64 func_external +.size indirect_func_external, 8 + +.section .data.indirect_func,"",@ +indirect_func: + .int64 foo + .size indirect_func, 8 + +# Test data relocations + +.section .data.data_addr,"",@ +data_addr: + .int32 data + .size data_addr, 4 + +# .. against external symbols + +.section .data.data_addr_external,"",@ +data_addr_external: + .int64 data_external + .size data_addr_external, 8 + +# .. including addends + +.section .data.extern_struct_internal_ptr,"",@ +extern_struct_internal_ptr: + .int32 extern_struct + 4 + .size extern_struct_internal_ptr, 4 + +# Test use of __stack_pointer + +.section .text,"",@ +foo: + # %ptr = alloca i32 + # %0 = load i32, ptr @data, align 4 + # %1 = load ptr, ptr @indirect_func, align 4 + # call i32 %1() + # ret i32 %0 + .functype foo () -> (i32) + .local i64, i32 + global.get __stack_pointer + i64.const 16 + i64.sub + local.tee 0 + global.set __stack_pointer + global.get __memory_base + i64.const data@MBREL + i64.add + i32.load 0 + local.set 1 + global.get indirect_func@GOT + i64.load 0 + i32.wrap_i64 + call_indirect () -> (i32) + drop + local.get 0 + i64.const 16 + i64.add + global.set __stack_pointer + local.get 1 + end_function + +get_func_address: + .functype get_func_address () -> (i64) + global.get func_external@GOT + end_function + +get_data_address: + .functype get_data_address () -> (i64) + global.get data_external@GOT + end_function + +get_local_func_address: + # Verify that a function which is otherwise not address taken *is* added to + # the wasm table with referenced via R_WASM_TABLE_INDEX_REL_SLEB64 + .functype get_local_func_address () -> (i64) + global.get __table_base + i64.const get_func_address@TBREL + i64.add + end_function + +.globl foo +.globl data +.globl indirect_func +.globl indirect_func_external +.globl data_addr +.globl data_addr_external +.globl extern_struct_internal_ptr +.globl get_data_address +.globl get_func_address +.globl get_local_func_address + +.hidden foo +.hidden data +.hidden get_data_address +.hidden get_func_address + +# Without this linking will fail because we import __stack_pointer (a mutable +# global). +# TODO(sbc): We probably want a nicer way to specify target_features section +# in assembly. +.section .custom_section.target_features,"",@ +.int8 1 +.int8 43 +.int8 15 +.ascii "mutable-globals" + +# check for dylink section at start + +# CHECK: Sections: +# CHECK-NEXT: - Type: CUSTOM +# CHECK-NEXT: Name: dylink.0 +# CHECK-NEXT: MemorySize: 36 +# CHECK-NEXT: MemoryAlignment: 2 +# CHECK-NEXT: TableSize: 2 +# CHECK-NEXT: TableAlignment: 0 +# CHECK-NEXT: Needed: [] +# CHECK-NEXT: - Type: TYPE + +# check for import of __table_base and __memory_base globals + +# CHECK: - Type: IMPORT +# CHECK-NEXT: Imports: +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: memory +# CHECK-NEXT: Kind: MEMORY +# CHECK-NEXT: Memory: +# CHECK-NEXT: Flags: [ IS_64 ] +# CHECK-NEXT: Minimum: 0x1 +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __indirect_function_table +# CHECK-NEXT: Kind: TABLE +# CHECK-NEXT: Table: +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: ElemType: FUNCREF +# CHECK-NEXT: Limits: +# CHECK-NEXT: Minimum: 0x2 +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __stack_pointer +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I64 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __memory_base +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I64 +# CHECK-NEXT: GlobalMutable: false +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __table_base +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I64 +# CHECK-NEXT: GlobalMutable: false +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: __table_base32 +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: false +# CHECK-NEXT: - Module: GOT.mem +# CHECK-NEXT: Field: indirect_func +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I64 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Module: GOT.func +# CHECK-NEXT: Field: func_external +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I64 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Module: GOT.mem +# CHECK-NEXT: Field: data_external +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I64 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Module: GOT.mem +# CHECK-NEXT: Field: extern_struct +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I64 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Type: FUNCTION + +# CHECK: - Type: EXPORT +# CHECK-NEXT: Exports: +# CHECK-NEXT: - Name: __wasm_call_ctors +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 0 + +# check for elem segment initialized with __table_base global as offset + +# CHECK: - Type: ELEM +# CHECK-NEXT: Segments: +# CHECK-NEXT: - Offset: +# CHECK-NEXT: Opcode: GLOBAL_GET +# CHECK-NEXT: Index: 3 +# CHECK-NEXT: Functions: [ 4, 3 ] + +# check the generated code in __wasm_call_ctors, __wasm_apply_data_relocs +# and __wasm_apply_data_relocs_tail functions + +# DIS: <__wasm_call_ctors>: +# DIS-EMPTY: +# DIS-NEXT: end + +# DIS: <__wasm_apply_data_relocs>: +# DIS-EMPTY: +# DIS-NEXT: i64.const 4 +# DIS-NEXT: global.get 1 +# DIS-NEXT: i64.add +# DIS-NEXT: global.get 5 +# DIS-NEXT: i64.store 0:p2align=2 +# DIS-NEXT: i64.const 12 +# DIS-NEXT: global.get 1 +# DIS-NEXT: i64.add +# DIS-NEXT: global.get 2 +# DIS-NEXT: i64.const 1 +# DIS-NEXT: i64.add +# DIS-NEXT: i64.store 0:p2align=2 +# DIS-NEXT: i64.const 20 +# DIS-NEXT: global.get 1 +# DIS-NEXT: i64.add +# DIS-NEXT: global.get 1 +# DIS-NEXT: i32.const 0 +# DIS-NEXT: i32.add +# DIS-NEXT: i32.store 0 +# DIS-NEXT: i64.const 24 +# DIS-NEXT: global.get 1 +# DIS-NEXT: i64.add +# DIS-NEXT: global.get 6 +# DIS-NEXT: i64.store 0:p2align=2 +# DIS-NEXT: i64.const 32 +# DIS-NEXT: global.get 1 +# DIS-NEXT: i64.add +# DIS-NEXT: global.get 7 +# DIS-NEXT: i32.const 4 +# DIS-NEXT: i32.add +# DIS-NEXT: i32.store 0 +# DIS-NEXT: end + +# DIS: <__wasm_apply_data_relocs_tail>: +# DIS-EMPTY: +# DIS-NEXT: end + +# check the data segment initialized with __memory_base global as offset + +# CHECK: - Type: DATA +# CHECK-NEXT: Segments: +# CHECK-NEXT: - SectionOffset: 6 +# CHECK-NEXT: InitFlags: 0 +# CHECK-NEXT: Offset: +# CHECK-NEXT: Opcode: GLOBAL_GET +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: Content: '020000000000000000000000010000000000000000000000000000000000000000000000' diff --git a/lld/test/wasm/tls-export.s b/lld/test/wasm/tls-export.s --- a/lld/test/wasm/tls-export.s +++ b/lld/test/wasm/tls-export.s @@ -1,48 +1,51 @@ -# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s -# RUN: wasm-ld -shared --experimental-pic -o %t.so %t.o -# RUN: obj2yaml %t.so | FileCheck %s - -.section .tdata.tls1,"",@ -.globl tls1 -.p2align 2 -tls1: - .int32 1 - .size tls1, 4 - -.section .custom_section.target_features,"",@ - .int8 3 - .int8 43 - .int8 7 - .ascii "atomics" - .int8 43 - .int8 11 - .ascii "bulk-memory" - .int8 43 - .int8 15 - .ascii "mutable-globals" - -# CHECK: ExportInfo: -# CHECK-NEXT: - Name: tls1 -# CHECK-NEXT: Flags: [ TLS ] -# CHECK-NEXT: - Type: TYPE - -# CHECK: - Type: GLOBAL -# CHECK-NEXT: Globals: -# CHECK-NEXT: - Index: 2 -# CHECK-NEXT: Type: I32 -# CHECK-NEXT: Mutable: false -# CHECK-NEXT: InitExpr: -# CHECK-NEXT: Opcode: I32_CONST -# CHECK-NEXT: Value: 0 - -# CHECK: - Type: EXPORT -# CHECK-NEXT: Exports: -# CHECK-NEXT: - Name: __wasm_call_ctors -# CHECK-NEXT: Kind: FUNCTION -# CHECK-NEXT: Index: 0 -# CHECK-NEXT: - Name: __wasm_apply_data_relocs -# CHECK-NEXT: Kind: FUNCTION -# CHECK-NEXT: Index: 1 -# CHECK-NEXT: - Name: tls1 -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: Index: 2 +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld -shared --experimental-pic -o %t.so %t.o +# RUN: obj2yaml %t.so | FileCheck %s + +.section .tdata.tls1,"",@ +.globl tls1 +.p2align 2 +tls1: + .int32 1 + .size tls1, 4 + +.section .custom_section.target_features,"",@ + .int8 3 + .int8 43 + .int8 7 + .ascii "atomics" + .int8 43 + .int8 11 + .ascii "bulk-memory" + .int8 43 + .int8 15 + .ascii "mutable-globals" + +# CHECK: ExportInfo: +# CHECK-NEXT: - Name: tls1 +# CHECK-NEXT: Flags: [ TLS ] +# CHECK-NEXT: - Type: TYPE + +# CHECK: - Type: GLOBAL +# CHECK-NEXT: Globals: +# CHECK-NEXT: - Index: 2 +# CHECK-NEXT: Type: I32 +# CHECK-NEXT: Mutable: false +# CHECK-NEXT: InitExpr: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 0 + +# CHECK: - Type: EXPORT +# CHECK-NEXT: Exports: +# CHECK-NEXT: - Name: __wasm_call_ctors +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: - Name: __wasm_apply_data_relocs +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: - Name: __wasm_apply_data_relocs_tail +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 2 +# CHECK-NEXT: - Name: tls1 +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: Index: 2 diff --git a/lld/test/wasm/tls-non-shared-memory.s b/lld/test/wasm/tls-non-shared-memory.s --- a/lld/test/wasm/tls-non-shared-memory.s +++ b/lld/test/wasm/tls-non-shared-memory.s @@ -1,165 +1,168 @@ -# Test that linking without shared memory causes __tls_base to be -# internalized. - -# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s - -.globaltype __tls_base, i32 - -.globl get_tls1 -get_tls1: - .functype get_tls1 () -> (i32) - global.get __tls_base - i32.const tls1@TLSREL - i32.add - end_function - -.globl get_tls1_got -get_tls1_got: - .functype get_tls1_got () -> (i32) - global.get tls1@GOT@TLS - end_function - -.section .data.no_tls,"",@ -.globl no_tls -.p2align 2 -no_tls: - .int32 42 - .size no_tls, 4 - -.section .tdata.tls1,"T",@ -.globl tls1 -.p2align 2 -tls1: - .int32 43 - .size tls1, 2 - -.section .custom_section.target_features,"",@ - .int8 2 - .int8 43 - .int8 7 - .ascii "atomics" - .int8 43 - .int8 11 - .ascii "bulk-memory" - -# RUN: wasm-ld --no-gc-sections --no-entry -o %t.wasm %t.o -# RUN: obj2yaml %t.wasm | FileCheck %s - -# RUN: wasm-ld --experimental-pic -shared -o %t.so %t.o -# RUN: obj2yaml %t.so | FileCheck %s --check-prefixes=SHARED,PIC - -# RUN: wasm-ld --experimental-pic --no-gc-sections --no-entry -pie -o %t-pie.wasm %t.o -# RUN: obj2yaml %t-pie.wasm | FileCheck %s --check-prefixes=PIE,PIC - -# RUN: wasm-ld --experimental-pic --features=atomics,bulk-memory,extended-const --no-gc-sections --no-entry -pie -o %t-extended-const.wasm %t.o -# RUN: obj2yaml %t-extended-const.wasm | FileCheck %s --check-prefixes=EXT-CONST - -# CHECK: - Type: GLOBAL -# __stack_pointer -# CHECK-NEXT: Globals: -# CHECK-NEXT: - Index: 0 -# CHECK-NEXT: Type: I32 -# CHECK-NEXT: Mutable: true -# CHECK-NEXT: InitExpr: -# CHECK-NEXT: Opcode: I32_CONST -# CHECK-NEXT: Value: 66576 -# __tls_base -# CHECK-NEXT: - Index: 1 -# CHECK-NEXT: Type: I32 -# CHECK-NEXT: Mutable: false -# CHECK-NEXT: InitExpr: -# CHECK-NEXT: Opcode: I32_CONST -# CHECK-NEXT: Value: 1024 -# GOT.data.internal.tls1 -# CHECK-NEXT: - Index: 2 -# CHECK-NEXT: Type: I32 -# CHECK-NEXT: Mutable: false -# CHECK-NEXT: InitExpr: -# CHECK-NEXT: Opcode: I32_CONST -# CHECK-NEXT: Value: 1024 -# CHECK-NEXT: - Type: EXPORT - -# CHECK: - Type: DATA -# .data -# CHECK-NEXT: Segments: -# CHECK-NEXT: - SectionOffset: 7 -# CHECK-NEXT: InitFlags: 0 -# CHECK-NEXT: Offset: -# CHECK-NEXT: Opcode: I32_CONST -# CHECK-NEXT: Value: 1024 -# CHECK-NEXT: Content: 2B000000 -# .tdata -# CHECK-NEXT: - SectionOffset: 17 -# CHECK-NEXT: InitFlags: 0 -# CHECK-NEXT: Offset: -# CHECK-NEXT: Opcode: I32_CONST -# CHECK-NEXT: Value: 1028 -# CHECK-NEXT: Content: 2A000000 -# CHECK-NEXT: - Type: CUSTOM - - -# In PIC mode we expect TLS data and non-TLS data to be merged into -# a single segment which is initialized via the __memory_base import - -# SHARED: - Type: IMPORT -# SHARED-NEXT: Imports: -# SHARED-NEXT: - Module: env -# SHARED-NEXT: Field: memory -# SHARED-NEXT: Kind: MEMORY -# SHARED-NEXT: Memory: -# SHARED-NEXT: Minimum: 0x1 -# SHARED-NEXT: - Module: env -# SHARED-NEXT: Field: __memory_base -# SHARED-NEXT: Kind: GLOBAL -# SHARED-NEXT: GlobalType: I32 - -# In SHARED mode we export the address of all data symbols. -# SHARED: - Type: EXPORT -# SHARED-NEXT: Exports: -# SHARED: - Name: tls1 -# SHARED-NEXT: Kind: GLOBAL -# SHARED: - Name: no_tls -# SHARED-NEXT: Kind: GLOBAL - -# In PIE mode we don't export data address by default. -# PIE: - Type: EXPORT -# PIE-NEXT: Exports: -# PIE-NEXT: - Name: memory -# PIE-NEXT: Kind: MEMORY -# PIE-NEXT: Index: 0 -# PIE-NEXT: - Name: __wasm_apply_data_relocs -# PIE-NEXT: Kind: FUNCTION -# PIE-NEXT: Index: 1 -# PIE-NEXT: - Type: - -# .tdata and .data are combined into single segment in PIC mode. -# PIC: - Type: DATA -# PIC-NEXT: Segments: -# PIC-NEXT: - SectionOffset: 6 -# PIC-NEXT: InitFlags: 0 -# PIC-NEXT: Offset: -# PIC-NEXT: Opcode: GLOBAL_GET -# PIC-NEXT: Index: {{\d*}} -# PIC-NEXT: Content: 2B0000002A000000 -# PIC-NEXT: - Type: CUSTOM - -# Unless we have extended-const, in which case the merging is not needed. -# The first segment is placed directly at `__memory_base` and the second -# one is offset from `__memory_base` using `i32.add` and a constant. - -# EXT-CONST: - Type: DATA -# EXT-CONST-NEXT: Segments: -# EXT-CONST-NEXT: - SectionOffset: 6 -# EXT-CONST-NEXT: InitFlags: 0 -# EXT-CONST-NEXT: Offset: -# EXT-CONST-NEXT: Opcode: GLOBAL_GET -# EXT-CONST-NEXT: Index: 1 -# EXT-CONST-NEXT: Content: 2B000000 -# EXT-CONST-NEXT: - SectionOffset: 18 -# EXT-CONST-NEXT: InitFlags: 0 -# EXT-CONST-NEXT: Offset: -# EXT-CONST-NEXT: Extended: true -# This instruction sequence decodes to: -# (global.get[0x23] 0x1 i32.const[0x41] 0x04 i32.add[0x6A] end[0x0b]) -# EXT-CONST-NEXT: Body: 230141046A0B -# EXT-CONST-NEXT: Content: 2A000000 +# Test that linking without shared memory causes __tls_base to be +# internalized. + +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s + +.globaltype __tls_base, i32 + +.globl get_tls1 +get_tls1: + .functype get_tls1 () -> (i32) + global.get __tls_base + i32.const tls1@TLSREL + i32.add + end_function + +.globl get_tls1_got +get_tls1_got: + .functype get_tls1_got () -> (i32) + global.get tls1@GOT@TLS + end_function + +.section .data.no_tls,"",@ +.globl no_tls +.p2align 2 +no_tls: + .int32 42 + .size no_tls, 4 + +.section .tdata.tls1,"T",@ +.globl tls1 +.p2align 2 +tls1: + .int32 43 + .size tls1, 2 + +.section .custom_section.target_features,"",@ + .int8 2 + .int8 43 + .int8 7 + .ascii "atomics" + .int8 43 + .int8 11 + .ascii "bulk-memory" + +# RUN: wasm-ld --no-gc-sections --no-entry -o %t.wasm %t.o +# RUN: obj2yaml %t.wasm | FileCheck %s + +# RUN: wasm-ld --experimental-pic -shared -o %t.so %t.o +# RUN: obj2yaml %t.so | FileCheck %s --check-prefixes=SHARED,PIC + +# RUN: wasm-ld --experimental-pic --no-gc-sections --no-entry -pie -o %t-pie.wasm %t.o +# RUN: obj2yaml %t-pie.wasm | FileCheck %s --check-prefixes=PIE,PIC + +# RUN: wasm-ld --experimental-pic --features=atomics,bulk-memory,extended-const --no-gc-sections --no-entry -pie -o %t-extended-const.wasm %t.o +# RUN: obj2yaml %t-extended-const.wasm | FileCheck %s --check-prefixes=EXT-CONST + +# CHECK: - Type: GLOBAL +# __stack_pointer +# CHECK-NEXT: Globals: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: Type: I32 +# CHECK-NEXT: Mutable: true +# CHECK-NEXT: InitExpr: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 66576 +# __tls_base +# CHECK-NEXT: - Index: 1 +# CHECK-NEXT: Type: I32 +# CHECK-NEXT: Mutable: false +# CHECK-NEXT: InitExpr: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 1024 +# GOT.data.internal.tls1 +# CHECK-NEXT: - Index: 2 +# CHECK-NEXT: Type: I32 +# CHECK-NEXT: Mutable: false +# CHECK-NEXT: InitExpr: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 1024 +# CHECK-NEXT: - Type: EXPORT + +# CHECK: - Type: DATA +# .data +# CHECK-NEXT: Segments: +# CHECK-NEXT: - SectionOffset: 7 +# CHECK-NEXT: InitFlags: 0 +# CHECK-NEXT: Offset: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 1024 +# CHECK-NEXT: Content: 2B000000 +# .tdata +# CHECK-NEXT: - SectionOffset: 17 +# CHECK-NEXT: InitFlags: 0 +# CHECK-NEXT: Offset: +# CHECK-NEXT: Opcode: I32_CONST +# CHECK-NEXT: Value: 1028 +# CHECK-NEXT: Content: 2A000000 +# CHECK-NEXT: - Type: CUSTOM + + +# In PIC mode we expect TLS data and non-TLS data to be merged into +# a single segment which is initialized via the __memory_base import + +# SHARED: - Type: IMPORT +# SHARED-NEXT: Imports: +# SHARED-NEXT: - Module: env +# SHARED-NEXT: Field: memory +# SHARED-NEXT: Kind: MEMORY +# SHARED-NEXT: Memory: +# SHARED-NEXT: Minimum: 0x1 +# SHARED-NEXT: - Module: env +# SHARED-NEXT: Field: __memory_base +# SHARED-NEXT: Kind: GLOBAL +# SHARED-NEXT: GlobalType: I32 + +# In SHARED mode we export the address of all data symbols. +# SHARED: - Type: EXPORT +# SHARED-NEXT: Exports: +# SHARED: - Name: tls1 +# SHARED-NEXT: Kind: GLOBAL +# SHARED: - Name: no_tls +# SHARED-NEXT: Kind: GLOBAL + +# In PIE mode we don't export data address by default. +# PIE: - Type: EXPORT +# PIE-NEXT: Exports: +# PIE-NEXT: - Name: memory +# PIE-NEXT: Kind: MEMORY +# PIE-NEXT: Index: 0 +# PIE-NEXT: - Name: __wasm_apply_data_relocs +# PIE-NEXT: Kind: FUNCTION +# PIE-NEXT: Index: 1 +# PIE-NEXT: - Name: __wasm_apply_data_relocs_tail +# PIE-NEXT: Kind: FUNCTION +# PIE-NEXT: Index: 2 +# PIE-NEXT: - Type: + +# .tdata and .data are combined into single segment in PIC mode. +# PIC: - Type: DATA +# PIC-NEXT: Segments: +# PIC-NEXT: - SectionOffset: 6 +# PIC-NEXT: InitFlags: 0 +# PIC-NEXT: Offset: +# PIC-NEXT: Opcode: GLOBAL_GET +# PIC-NEXT: Index: {{\d*}} +# PIC-NEXT: Content: 2B0000002A000000 +# PIC-NEXT: - Type: CUSTOM + +# Unless we have extended-const, in which case the merging is not needed. +# The first segment is placed directly at `__memory_base` and the second +# one is offset from `__memory_base` using `i32.add` and a constant. + +# EXT-CONST: - Type: DATA +# EXT-CONST-NEXT: Segments: +# EXT-CONST-NEXT: - SectionOffset: 6 +# EXT-CONST-NEXT: InitFlags: 0 +# EXT-CONST-NEXT: Offset: +# EXT-CONST-NEXT: Opcode: GLOBAL_GET +# EXT-CONST-NEXT: Index: 1 +# EXT-CONST-NEXT: Content: 2B000000 +# EXT-CONST-NEXT: - SectionOffset: 18 +# EXT-CONST-NEXT: InitFlags: 0 +# EXT-CONST-NEXT: Offset: +# EXT-CONST-NEXT: Extended: true +# This instruction sequence decodes to: +# (global.get[0x23] 0x1 i32.const[0x41] 0x04 i32.add[0x6A] end[0x0b]) +# EXT-CONST-NEXT: Body: 230141046A0B +# EXT-CONST-NEXT: Content: 2A000000 diff --git a/lld/test/wasm/unresolved-symbols-dynamic.s b/lld/test/wasm/unresolved-symbols-dynamic.s --- a/lld/test/wasm/unresolved-symbols-dynamic.s +++ b/lld/test/wasm/unresolved-symbols-dynamic.s @@ -1,84 +1,86 @@ -# Unresolve data symbols are allowing under import-dynamic when GOT -# relocations are used -# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t-dynamic.o -# RUN: wasm-ld %t-dynamic.o -o %t.wasm --unresolved-symbols=import-dynamic 2>&1 | FileCheck -check-prefix=WARN %s -# WARN: wasm-ld: warning: dynamic imports are not yet stable (--unresolved-symbols=import-dynamic) -# RUN: obj2yaml %t.wasm | FileCheck %s - -.functype undef () -> () - -.globl get_data_addr -get_data_addr: - .functype get_data_addr () -> (i32) - global.get undef_data@GOT - return - end_function - -.globl get_func_addr -get_func_addr: - .functype get_func_addr () -> (i32) - global.get undef@GOT - return - end_function - -.globl _start -_start: - .functype _start () -> () - call undef - call get_func_addr - drop - call get_data_addr - i32.load data_ptr - drop - end_function - -.section .data.data_ptr,"",@ -data_ptr: - .int32 data_external+42 - .size data_ptr, 4 - -.size data_external, 4 - -# CHECK: - Type: IMPORT -# CHECK-NEXT: Imports: -# CHECK-NEXT: - Module: env -# CHECK-NEXT: Field: undef -# CHECK-NEXT: Kind: FUNCTION -# CHECK-NEXT: SigIndex: 0 -# CHECK-NEXT: - Module: GOT.mem -# CHECK-NEXT: Field: undef_data -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: true -# CHECK-NEXT: - Module: GOT.func -# CHECK-NEXT: Field: undef -# CHECK-NEXT: Kind: GLOBAL -# CHECK-NEXT: GlobalType: I32 -# CHECK-NEXT: GlobalMutable: true - -# CHECK: - Type: CUSTOM -# CHECK-NEXT: Name: name -# CHECK-NEXT: FunctionNames: -# CHECK-NEXT: - Index: 0 -# CHECK-NEXT: Name: undef -# CHECK-NEXT: - Index: 1 -# CHECK-NEXT: Name: __wasm_apply_data_relocs -# CHECK-NEXT: - Index: 2 -# CHECK-NEXT: Name: get_data_addr -# CHECK-NEXT: - Index: 3 -# CHECK-NEXT: Name: get_func_addr -# CHECK-NEXT: - Index: 4 -# CHECK-NEXT: Name: _start -# CHECK-NEXT: GlobalNames: -# CHECK-NEXT: - Index: 0 -# CHECK-NEXT: Name: undef_data -# CHECK-NEXT: - Index: 1 -# CHECK-NEXT: Name: undef -# CHECK-NEXT: - Index: 2 -# CHECK-NEXT: Name: data_external -# CHECK-NEXT: - Index: 3 -# CHECK-NEXT: Name: __stack_pointer -# CHECK-NEXT: DataSegmentNames: -# CHECK-NEXT: - Index: 0 -# CHECK-NEXT: Name: .data -# CHECK-NEXT:... +# Unresolve data symbols are allowing under import-dynamic when GOT +# relocations are used +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t-dynamic.o +# RUN: wasm-ld %t-dynamic.o -o %t.wasm --unresolved-symbols=import-dynamic 2>&1 | FileCheck -check-prefix=WARN %s +# WARN: wasm-ld: warning: dynamic imports are not yet stable (--unresolved-symbols=import-dynamic) +# RUN: obj2yaml %t.wasm | FileCheck %s + +.functype undef () -> () + +.globl get_data_addr +get_data_addr: + .functype get_data_addr () -> (i32) + global.get undef_data@GOT + return + end_function + +.globl get_func_addr +get_func_addr: + .functype get_func_addr () -> (i32) + global.get undef@GOT + return + end_function + +.globl _start +_start: + .functype _start () -> () + call undef + call get_func_addr + drop + call get_data_addr + i32.load data_ptr + drop + end_function + +.section .data.data_ptr,"",@ +data_ptr: + .int32 data_external+42 + .size data_ptr, 4 + +.size data_external, 4 + +# CHECK: - Type: IMPORT +# CHECK-NEXT: Imports: +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: undef +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: SigIndex: 0 +# CHECK-NEXT: - Module: GOT.mem +# CHECK-NEXT: Field: undef_data +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: true +# CHECK-NEXT: - Module: GOT.func +# CHECK-NEXT: Field: undef +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: GlobalType: I32 +# CHECK-NEXT: GlobalMutable: true + +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: name +# CHECK-NEXT: FunctionNames: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: Name: undef +# CHECK-NEXT: - Index: 1 +# CHECK-NEXT: Name: __wasm_apply_data_relocs +# CHECK-NEXT: - Index: 2 +# CHECK-NEXT: Name: __wasm_apply_data_relocs_tail +# CHECK-NEXT: - Index: 3 +# CHECK-NEXT: Name: get_data_addr +# CHECK-NEXT: - Index: 4 +# CHECK-NEXT: Name: get_func_addr +# CHECK-NEXT: - Index: 5 +# CHECK-NEXT: Name: _start +# CHECK-NEXT: GlobalNames: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: Name: undef_data +# CHECK-NEXT: - Index: 1 +# CHECK-NEXT: Name: undef +# CHECK-NEXT: - Index: 2 +# CHECK-NEXT: Name: data_external +# CHECK-NEXT: - Index: 3 +# CHECK-NEXT: Name: __stack_pointer +# CHECK-NEXT: DataSegmentNames: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: Name: .data +# CHECK-NEXT:... diff --git a/lld/wasm/CMakeLists.txt b/lld/wasm/CMakeLists.txt --- a/lld/wasm/CMakeLists.txt +++ b/lld/wasm/CMakeLists.txt @@ -12,6 +12,7 @@ OutputSections.cpp OutputSegment.cpp Relocations.cpp + SplitApplyDataRelocsStrategy.cpp SymbolTable.cpp Symbols.cpp SyntheticSections.cpp diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -67,6 +67,9 @@ bool ltoDebugPassManager; UnresolvedPolicy unresolvedSymbols; + // https://github.com/v8/v8/blob/master/src/wasm/wasm-limits.h#L47 + uint64_t maxFunctionSize = 7'654'321; + llvm::StringRef entry; llvm::StringRef mapFile; llvm::StringRef outputFile; diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -763,6 +763,12 @@ "__wasm_apply_data_relocs", WASM_SYMBOL_VISIBILITY_DEFAULT | WASM_SYMBOL_EXPORTED, make(nullSignature, "__wasm_apply_data_relocs")); + + WasmSym::applyDataRelocsTail = symtab->addSyntheticFunction( + "__wasm_apply_data_relocs_tail", + WASM_SYMBOL_VISIBILITY_DEFAULT | WASM_SYMBOL_EXPORTED, + make(nullSignature, + "__wasm_apply_data_relocs_tail")); } } diff --git a/lld/wasm/FunctionSplitter.h b/lld/wasm/FunctionSplitter.h new file mode 100644 --- /dev/null +++ b/lld/wasm/FunctionSplitter.h @@ -0,0 +1,25 @@ +#ifndef LLD_WASM_FUNCTION_SPLITTER_H +#define LLD_WASM_FUNCTION_SPLITTER_H + +#include "SplitStrategy.h" + +#include +#include +#include + +namespace lld::wasm { + +class FunctionSplitter { +public: + FunctionSplitter(std::unique_ptr strategy) + : split_strategy_(std::move(strategy)) {} + + SplitStrategy::SplittedFunctions split() { return split_strategy_->split(); } + +private: + std::unique_ptr split_strategy_; +}; + +} // namespace lld::wasm + +#endif diff --git a/lld/wasm/SplitApplyDataRelocsStrategy.h b/lld/wasm/SplitApplyDataRelocsStrategy.h new file mode 100644 --- /dev/null +++ b/lld/wasm/SplitApplyDataRelocsStrategy.h @@ -0,0 +1,30 @@ +#ifndef LLD_WASM_SPLIT_APPLY_DATA_RELOCS_STRATEGY_H +#define LLD_WASM_SPLIT_APPLY_DATA_RELOCS_STRATEGY_H + +#include "SplitStrategy.h" + +namespace lld::wasm { + +class SplitApplyDataRelocsStrategy final : public SplitStrategy { +public: + SplitApplyDataRelocsStrategy(const std::string &functionBody); + virtual ~SplitApplyDataRelocsStrategy() = default; + + // Splits the function up into 2 functions. Cuts off the instructions + // that exceed the function size limit and put them into the second function. + // The first function calls the second one at the end. + // The last instruction that is gonna be left in the first function + // is WASM_OPCODE_I32_STORE/WASM_OPCODE_I64_STORE (+ align & offset) + SplittedFunctions split() override; + +private: + size_t findPositionToCutInstructionsFrom(unsigned instruction_opcode) const; + void addApplyDataRelocsTailCall(std::string &functionBody, size_t pos) const; + void addCutOffInstructions(std::string &functionBody, size_t pos) const; + + const std::string &function_to_split_; +}; + +} // namespace lld::wasm + +#endif diff --git a/lld/wasm/SplitApplyDataRelocsStrategy.cpp b/lld/wasm/SplitApplyDataRelocsStrategy.cpp new file mode 100644 --- /dev/null +++ b/lld/wasm/SplitApplyDataRelocsStrategy.cpp @@ -0,0 +1,68 @@ +#include "SplitApplyDataRelocsStrategy.h" +#include "Config.h" +#include "Symbols.h" +#include "WriterUtils.h" +#include "llvm/BinaryFormat/Wasm.h" +#include "llvm/Support/raw_ostream.h" + +namespace lld::wasm { + +SplitApplyDataRelocsStrategy::SplitApplyDataRelocsStrategy( + const std::string &functionBody) + : function_to_split_(functionBody) {} + +SplitApplyDataRelocsStrategy::SplittedFunctions +SplitApplyDataRelocsStrategy::split() { + if (function_to_split_.size() < config->maxFunctionSize) { + throw "The function is intented to only split functions that exceed" + "the function size limit"; + } + + using namespace llvm::wasm; + + SplittedFunctions functions; + const bool is64 = config->is64.value_or(false); + const unsigned store_opcode = + is64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE; + + const size_t pos = findPositionToCutInstructionsFrom(store_opcode); + addApplyDataRelocsTailCall(functions.first, pos); + addCutOffInstructions(functions.second, pos); + + return functions; +} + +size_t SplitApplyDataRelocsStrategy::findPositionToCutInstructionsFrom( + unsigned instruction_opcode) const { + // Skip some instructions to leave the place for CALL instruction and others + constexpr size_t skip_instruction_count = 10; + const size_t pos = function_to_split_.rfind( + instruction_opcode, config->maxFunctionSize - skip_instruction_count); + if (pos == std::string::npos) + throw "No instruction opcode found"; + + // includes the following instructions: store + align + offset + constexpr size_t offset = 3; + + return pos + offset; +} + +void SplitApplyDataRelocsStrategy::addApplyDataRelocsTailCall( + std::string &functionBody, size_t pos) const { + llvm::raw_string_ostream first_os(functionBody); + functionBody = function_to_split_.substr(0, pos); + + writeU8(first_os, llvm::wasm::WASM_OPCODE_CALL, "CALL"); + writeUleb128(first_os, WasmSym::applyDataRelocsTail->getFunctionIndex(), + "function index"); + writeU8(first_os, llvm::wasm::WASM_OPCODE_END, "END"); +} + +void SplitApplyDataRelocsStrategy::addCutOffInstructions( + std::string &functionBody, size_t pos) const { + llvm::raw_string_ostream second_os(functionBody); + writeUleb128(second_os, 0, "num locals"); + functionBody.append(function_to_split_.substr(pos)); +} + +} // namespace lld::wasm diff --git a/lld/wasm/SplitStrategy.h b/lld/wasm/SplitStrategy.h new file mode 100644 --- /dev/null +++ b/lld/wasm/SplitStrategy.h @@ -0,0 +1,19 @@ +#ifndef LLD_WASM_SPLIT_STRATEGY_H +#define LLD_WASM_SPLIT_STRATEGY_H + +#include +#include + +namespace lld::wasm { + +class SplitStrategy { +public: + using SplittedFunctions = std::pair; + + virtual ~SplitStrategy() = default; + virtual SplittedFunctions split() = 0; +}; + +} // namespace lld::wasm + +#endif diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -573,6 +573,15 @@ // Function that applies relocations to data segment post-instantiation. static DefinedFunction *applyDataRelocs; + // __wasm_apply_data_relocs_tail + // __wasm_apply_data_relocs can grow drastically exceeding the function + // size limit. In that case, we split it up into 2 functions, so that + // the extra number of instructions go to applyDataRelocsTail. + // This function is called only from applyDataRelocs and only if + // the latter exceeds the function size limit. The rest of the time this + // function is empty. + static DefinedFunction *applyDataRelocsTail; + // __wasm_apply_global_relocs // Function that applies relocations to wasm globals post-instantiation. // Unlike __wasm_apply_data_relocs this needs to run on every thread. diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -76,6 +76,7 @@ DefinedFunction *WasmSym::callDtors; DefinedFunction *WasmSym::initMemory; DefinedFunction *WasmSym::applyDataRelocs; +DefinedFunction *WasmSym::applyDataRelocsTail; DefinedFunction *WasmSym::applyGlobalRelocs; DefinedFunction *WasmSym::applyGlobalTLSRelocs; DefinedFunction *WasmSym::initTLS; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -8,12 +8,14 @@ #include "Writer.h" #include "Config.h" +#include "FunctionSplitter.h" #include "InputChunks.h" #include "InputElement.h" #include "MapFile.h" #include "OutputSections.h" #include "OutputSegment.h" #include "Relocations.h" +#include "SplitApplyDataRelocsStrategy.h" #include "SymbolTable.h" #include "SyntheticSections.h" #include "WriterUtils.h" @@ -68,6 +70,8 @@ void createCommandExportWrappers(); void createCommandExportWrapper(uint32_t functionIndex, DefinedFunction *f); + std::string createEmptyFunctionBody() const; + void assignIndexes(); void populateSymtab(); void populateProducers(); @@ -1338,7 +1342,26 @@ writeU8(os, WASM_OPCODE_END, "END"); } - createFunction(WasmSym::applyDataRelocs, bodyContent); + if (bodyContent.size() < config->maxFunctionSize) { + createFunction(WasmSym::applyDataRelocs, bodyContent); + createFunction(WasmSym::applyDataRelocsTail, createEmptyFunctionBody()); + } else { + FunctionSplitter splitter( + std::make_unique(bodyContent)); + auto &&[firstFunctionBody, secondFunctionBody] = splitter.split(); + createFunction(WasmSym::applyDataRelocs, firstFunctionBody); + createFunction(WasmSym::applyDataRelocsTail, secondFunctionBody); + } +} + +std::string Writer::createEmptyFunctionBody() const { + std::string bodyContent; + raw_string_ostream os(bodyContent); + + writeUleb128(os, 0, "num locals"); + writeU8(os, WASM_OPCODE_END, "END"); + + return bodyContent; } // Similar to createApplyDataRelocationsFunction but generates relocation code