| // Tests creating a bundle from multiple modules using nested module format. |
| // Named modules are wrapped in nested module containers. |
| // External declarations are preserved (not resolved in bundle mode). |
| |
| // RUN: iree-link --bundle \ |
| // RUN: --link-module=%S/iree-link-module-a.mlir \ |
| // RUN: --link-module=%S/iree-link-module-b.mlir \ |
| // RUN: -o - | FileCheck %s |
| |
| // CHECK: module { |
| // CHECK: module @module_a { |
| // CHECK: util.func private @module_b.process |
| // CHECK: util.func public @compute |
| // CHECK: flow.executable private @compute_dispatch_0 |
| // CHECK: } |
| // CHECK: module @module_b { |
| // CHECK: util.func public @helper |
| // CHECK: util.func public @process |
| // CHECK: } |
| // CHECK: } |
| |
| // ----- |
| |
| // Tests that bytecode output works correctly (auto-detected from .mlirbc |
| // extension). |
| |
| // RUN: iree-link --bundle \ |
| // RUN: --link-module=%S/iree-link-module-a.mlir \ |
| // RUN: --link-module=%S/iree-link-module-b.mlir \ |
| // RUN: -o %t.mlirbc |
| // RUN: iree-opt %t.mlirbc | FileCheck %s --check-prefix=BYTECODE |
| |
| // BYTECODE: module { |
| // BYTECODE: module @module_a { |
| // BYTECODE: util.func public @compute |
| // BYTECODE: } |
| // BYTECODE: module @module_b { |
| // BYTECODE: util.func public @helper |
| // BYTECODE: util.func public @process |
| // BYTECODE: } |
| // BYTECODE: } |
| |
| // ----- |
| |
| // Tests that private function shadowing works correctly in bundles. |
| // Both anonymous modules have conflicting private @scale_factor() functions. |
| // mergeModuleInto renames the second module's symbols to avoid conflicts. |
| |
| // RUN: iree-link --bundle \ |
| // RUN: --link-module=%S/iree-link-module-c.mlir \ |
| // RUN: --link-module=%S/iree-link-anonymous.mlir \ |
| // RUN: -o - | FileCheck %s --check-prefix=SHADOW |
| |
| // SHADOW: module { |
| // SHADOW-DAG: util.func private @scale_factor() -> i32 |
| // SHADOW-DAG: util.func public @subtract |
| // SHADOW-DAG: util.func public @double |
| // SHADOW-DAG: util.func private @scale_factor_0() -> i32 |
| // SHADOW-DAG: util.func public @test_anonymous |
| // SHADOW: } |
| |
| // ----- |
| |
| // Tests creating a bundle with a mix of named and anonymous modules. |
| // Named modules (a, b) are wrapped in nested modules. |
| // Anonymous module (c) operations are placed at the top level. |
| |
| // RUN: iree-link --bundle \ |
| // RUN: --link-module=%S/iree-link-module-a.mlir \ |
| // RUN: --link-module=%S/iree-link-module-b.mlir \ |
| // RUN: --link-module=%S/iree-link-module-c.mlir \ |
| // RUN: -o - | FileCheck %s --check-prefix=ANONYMOUS |
| |
| // ANONYMOUS: module { |
| // ANONYMOUS: module @module_a { |
| // ANONYMOUS: util.func private @module_b.process |
| // ANONYMOUS: util.func public @compute |
| // ANONYMOUS: } |
| // ANONYMOUS: module @module_b { |
| // ANONYMOUS: util.func public @helper |
| // ANONYMOUS: util.func public @process |
| // ANONYMOUS: } |
| // ANONYMOUS: util.func private @scale_factor |
| // ANONYMOUS: util.func public @subtract |
| // ANONYMOUS: util.func public @double |
| // ANONYMOUS: } |
| |
| // ----- |
| |
| // Tests creating an empty bundle with no input modules. |
| // Should produce an empty module. |
| |
| // RUN: iree-link --bundle -o - | FileCheck %s --check-prefix=EMPTY |
| |
| // EMPTY: module { |
| // EMPTY: } |
| |
| // ----- |
| |
| // Tests that globals with initializers are correctly handled during bundling. |
| // Two anonymous modules have conflicting @state globals with same initial value |
| // but different initializers. Both should be preserved with one renamed. |
| |
| // RUN: iree-link --bundle \ |
| // RUN: --link-module=%S/iree-link-globals-a.mlir \ |
| // RUN: --link-module=%S/iree-link-globals-b.mlir \ |
| // RUN: -o - | FileCheck %s --check-prefix=GLOBALS |
| |
| // First global, initializer, and function (keeps original names). |
| // GLOBALS: util.global private mutable @state = dense<0.000000e+00> : tensor<f32> |
| // GLOBALS-NEXT: util.initializer { |
| // GLOBALS-NEXT: %[[C1:.+]] = arith.constant dense<1.000000e+00> : tensor<f32> |
| // GLOBALS-NEXT: util.global.store %[[C1]], @state : tensor<f32> |
| // GLOBALS-NEXT: util.return |
| // GLOBALS-NEXT: } |
| // GLOBALS-NEXT: util.func private @get_state() -> tensor<f32> { |
| // GLOBALS-NEXT: %[[LOAD1:.+]] = util.global.load @state : tensor<f32> |
| // GLOBALS-NEXT: util.return %[[LOAD1]] : tensor<f32> |
| // GLOBALS-NEXT: } |
| |
| // Second global, initializer, and function (renamed to @state_0/@get_state_0). |
| // GLOBALS-NEXT: util.global private mutable @state_0 = dense<0.000000e+00> : tensor<f32> |
| // GLOBALS-NEXT: util.initializer { |
| // GLOBALS-NEXT: %[[C2:.+]] = arith.constant dense<2.000000e+00> : tensor<f32> |
| // GLOBALS-NEXT: util.global.store %[[C2]], @state_0 : tensor<f32> |
| // GLOBALS-NEXT: util.return |
| // GLOBALS-NEXT: } |
| // GLOBALS-NEXT: util.func private @get_state_0() -> tensor<f32> { |
| // GLOBALS-NEXT: %[[LOAD2:.+]] = util.global.load @state_0 : tensor<f32> |
| // GLOBALS-NEXT: util.return %[[LOAD2]] : tensor<f32> |
| // GLOBALS-NEXT: } |
| |
| // ----- |
| |
| // Tests three-way global conflict with different initial values. |
| // All three globals should be preserved with unique names. |
| |
| // RUN: iree-link --bundle \ |
| // RUN: --link-module=%S/iree-link-globals-a.mlir \ |
| // RUN: --link-module=%S/iree-link-globals-b.mlir \ |
| // RUN: --link-module=%S/iree-link-globals-c.mlir \ |
| // RUN: -o - | FileCheck %s --check-prefix=THREEWAY |
| |
| // First module: global, initializer, and function (keeps original names). |
| // THREEWAY: util.global private mutable @state = dense<0.000000e+00> : tensor<f32> |
| // THREEWAY-NEXT: util.initializer { |
| // THREEWAY-NEXT: %[[C1:.+]] = arith.constant dense<1.000000e+00> : tensor<f32> |
| // THREEWAY-NEXT: util.global.store %[[C1]], @state : tensor<f32> |
| // THREEWAY-NEXT: util.return |
| // THREEWAY-NEXT: } |
| // THREEWAY-NEXT: util.func private @get_state() -> tensor<f32> { |
| // THREEWAY-NEXT: %[[LOAD1:.+]] = util.global.load @state : tensor<f32> |
| // THREEWAY-NEXT: util.return %[[LOAD1]] : tensor<f32> |
| // THREEWAY-NEXT: } |
| |
| // Second module: global, initializer, and function (renamed to _0 suffix). |
| // THREEWAY-NEXT: util.global private mutable @state_0 = dense<0.000000e+00> : tensor<f32> |
| // THREEWAY-NEXT: util.initializer { |
| // THREEWAY-NEXT: %[[C2:.+]] = arith.constant dense<2.000000e+00> : tensor<f32> |
| // THREEWAY-NEXT: util.global.store %[[C2]], @state_0 : tensor<f32> |
| // THREEWAY-NEXT: util.return |
| // THREEWAY-NEXT: } |
| // THREEWAY-NEXT: util.func private @get_state_0() -> tensor<f32> { |
| // THREEWAY-NEXT: %[[LOAD2:.+]] = util.global.load @state_0 : tensor<f32> |
| // THREEWAY-NEXT: util.return %[[LOAD2]] : tensor<f32> |
| // THREEWAY-NEXT: } |
| |
| // Third module: global, initializer, and function (renamed to _1 suffix). |
| // THREEWAY-NEXT: util.global private mutable @state_1 = dense<5.000000e+00> : tensor<f32> |
| // THREEWAY-NEXT: util.initializer { |
| // THREEWAY-NEXT: %[[C3:.+]] = arith.constant dense<3.000000e+00> : tensor<f32> |
| // THREEWAY-NEXT: util.global.store %[[C3]], @state_1 : tensor<f32> |
| // THREEWAY-NEXT: util.return |
| // THREEWAY-NEXT: } |
| // THREEWAY-NEXT: util.func private @get_state_1() -> tensor<f32> { |
| // THREEWAY-NEXT: %[[LOAD3:.+]] = util.global.load @state_1 : tensor<f32> |
| // THREEWAY-NEXT: util.return %[[LOAD3]] : tensor<f32> |
| // THREEWAY-NEXT: } |