Skip to content

Commit eabf522

Browse files
hunhoffeclaude
andcommitted
[test] Add aiecc and npu-xrt tests for func-level link_with
aiecc dialect tests (test/aiecc/): - cpp_link_with.mlir: extend existing test with func-level link_with variant - cpp_link_with_func_level.mlir: basic func.func link_with → CoreOp link_files - cpp_link_with_both_attrs.mlir: CoreOp + func.func link_with coexist - cpp_link_with_deprecation.mlir: CoreOp-level link_with deprecation warning - cpp_link_with_emitter_fallback.mlir: BCF/LdScript emit from link_files - cpp_link_with_indirect_call.mlir: indirect call chain resolved by pass - cpp_link_with_mixed.mlir: mixed kernel .o per-core - cpp_link_with_shared_func.mlir: same func.func called by multiple cores - cpp_link_with_unused_func.mlir: unused func.func not added - cpp_multi_link_with.mlir: multiple link_with attrs on different funcs npu-xrt end-to-end tests (require physical NPU): - add_one_func_link_with_{chess,peano}: single kernel via func-level link_with - add_one_scale_func_link_with_{chess,peano}: two kernels (add_one + scale_by_two) each with its own func.func link_with, exercising multi-.o linking per core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5039f08 commit eabf522

28 files changed

+1415
-2
lines changed

test/aiecc/cpp_link_with.mlir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module {
2929
aie.objectfifo @of_in(%tile_0_0, {%tile_0_2}, 2 : i32) : !aie.objectfifo<memref<16xi32>>
3030
aie.objectfifo @of_out(%tile_0_2, {%tile_0_0}, 2 : i32) : !aie.objectfifo<memref<16xi32>>
3131

32-
func.func private @external_func(memref<16xi32>, memref<16xi32>)
32+
func.func private @external_func(memref<16xi32>, memref<16xi32>) attributes {link_with = "external.o"}
3333

3434
%core_0_2 = aie.core(%tile_0_2) {
3535
%subview_in = aie.objectfifo.acquire @of_in(Consume, 1) : !aie.objectfifosubview<memref<16xi32>>
@@ -43,7 +43,7 @@ module {
4343
aie.objectfifo.release @of_in(Consume, 1)
4444
aie.objectfifo.release @of_out(Produce, 1)
4545
aie.end
46-
} {link_with = "external.o"}
46+
}
4747

4848
aie.runtime_sequence(%in : memref<16xi32>, %out : memref<16xi32>) {
4949
%c0 = arith.constant 0 : i64
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===- cpp_link_with_both_attrs.mlir ----------------------------*- MLIR -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// (c) Copyright 2026 Advanced Micro Devices, Inc.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
// Test that a core with both the deprecated 'link_with' scalar attr AND the
12+
// canonical 'link_files' array attr on the same CoreOp is rejected by the
13+
// CoreOp verifier.
14+
15+
// RUN: aie-opt --verify-diagnostics %s
16+
17+
module {
18+
aie.device(npu1_1col) {
19+
%tile_0_2 = aie.tile(0, 2)
20+
21+
// expected-error@+1 {{cannot specify both 'link_with' (deprecated) and 'link_files'}}
22+
%core_0_2 = aie.core(%tile_0_2) {
23+
aie.end
24+
} {link_with = "a.o", link_files = ["b.o"]}
25+
}
26+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===- cpp_link_with_deprecation.mlir --------------------------*- MLIR -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// Copyright (C) 2026, Advanced Micro Devices, Inc.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
// Test that core-level link_with still compiles but emits a deprecation warning,
12+
// and that the pass migrates the attribute to link_files on the core.
13+
14+
// RUN: aie-opt --verify-diagnostics --aie-assign-core-link-files %s
15+
// RUN: aie-opt --verify-diagnostics --aie-assign-core-link-files %s | FileCheck %s --check-prefix=MIGRATED
16+
17+
// Verify the pass migrated the deprecated core-level attr into link_files and
18+
// removed link_with from the core.
19+
// MIGRATED: link_files = ["legacy.o"]
20+
// MIGRATED-NOT: link_with = "legacy.o"
21+
22+
module {
23+
aie.device(npu1_1col) {
24+
%tile_0_0 = aie.tile(0, 0)
25+
%tile_0_2 = aie.tile(0, 2)
26+
27+
aie.objectfifo @of(%tile_0_0, {%tile_0_2}, 2 : i32) : !aie.objectfifo<memref<16xi32>>
28+
29+
// expected-warning@+1 {{link_with on aie.core is deprecated; attach link_with to the func.func declaration instead}}
30+
%core_0_2 = aie.core(%tile_0_2) {
31+
%buf = aie.objectfifo.acquire @of(Consume, 1) : !aie.objectfifosubview<memref<16xi32>>
32+
aie.objectfifo.release @of(Consume, 1)
33+
aie.end
34+
} {link_with = "legacy.o"}
35+
36+
aie.runtime_sequence() {}
37+
}
38+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===- cpp_link_with_emitter_fallback.mlir ---------------------*- MLIR -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// Copyright (C) 2026, Advanced Micro Devices, Inc.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
// Test the deprecated fallback path in the ldscript and BCF emitters:
12+
// when a core still has a core-level link_with (and no link_files), both
13+
// emitters should still emit the correct entry without running
14+
// aie-assign-core-link-files first.
15+
16+
// RUN: aie-translate --aie-generate-ldscript --tilecol=0 --tilerow=2 %s | FileCheck %s --check-prefix=LDSCRIPT
17+
// RUN: aie-translate --aie-generate-bcf --tilecol=0 --tilerow=2 %s | FileCheck %s --check-prefix=BCF
18+
19+
// LDSCRIPT: INPUT(fallback.o)
20+
// BCF: _include _file fallback.o
21+
22+
// Use a bare core without objectfifo so no lowering is needed before
23+
// aie-translate can generate the ldscript/BCF.
24+
25+
module {
26+
aie.device(npu1_1col) {
27+
%tile_0_2 = aie.tile(0, 2)
28+
29+
// Core keeps the old core-level link_with (no pass run, no link_files set).
30+
%core_0_2 = aie.core(%tile_0_2) {
31+
aie.end
32+
} {link_with = "fallback.o"}
33+
}
34+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===- cpp_link_with_func_level.mlir ---------------------------*- MLIR -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// Copyright (C) 2026, Advanced Micro Devices, Inc.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
// Canonical new style: link_with is on func.func, not on aie.core.
12+
// Verify that AIEAssignCoreLinkFiles populates link_files on the core and
13+
// that the ldscript/BCF emitters produce the correct entries.
14+
15+
// RUN: aie-opt --verify-diagnostics --aie-assign-core-link-files %s | FileCheck %s --check-prefix=OPT
16+
// RUN: aie-opt --verify-diagnostics --aie-assign-core-link-files %s | aie-translate --aie-generate-ldscript --tilecol=0 --tilerow=2 | FileCheck %s --check-prefix=LDSCRIPT
17+
// RUN: aie-opt --verify-diagnostics --aie-assign-core-link-files %s | aie-translate --aie-generate-bcf --tilecol=0 --tilerow=2 | FileCheck %s --check-prefix=BCF
18+
19+
// OPT: link_files = ["f.o"]
20+
21+
// LDSCRIPT: INPUT(f.o)
22+
23+
// BCF: _include _file f.o
24+
25+
module {
26+
aie.device(npu1_1col) {
27+
%tile_0_0 = aie.tile(0, 0)
28+
%tile_0_2 = aie.tile(0, 2)
29+
30+
aie.objectfifo @of_in(%tile_0_0, {%tile_0_2}, 2 : i32) : !aie.objectfifo<memref<16xi32>>
31+
aie.objectfifo @of_out(%tile_0_2, {%tile_0_0}, 2 : i32) : !aie.objectfifo<memref<16xi32>>
32+
33+
func.func private @f(memref<16xi32>, memref<16xi32>) attributes {link_with = "f.o"}
34+
35+
%core_0_2 = aie.core(%tile_0_2) {
36+
%subview_in = aie.objectfifo.acquire @of_in(Consume, 1) : !aie.objectfifosubview<memref<16xi32>>
37+
%elem_in = aie.objectfifo.subview.access %subview_in[0] : !aie.objectfifosubview<memref<16xi32>> -> memref<16xi32>
38+
39+
%subview_out = aie.objectfifo.acquire @of_out(Produce, 1) : !aie.objectfifosubview<memref<16xi32>>
40+
%elem_out = aie.objectfifo.subview.access %subview_out[0] : !aie.objectfifosubview<memref<16xi32>> -> memref<16xi32>
41+
42+
func.call @f(%elem_in, %elem_out) : (memref<16xi32>, memref<16xi32>) -> ()
43+
44+
aie.objectfifo.release @of_in(Consume, 1)
45+
aie.objectfifo.release @of_out(Produce, 1)
46+
aie.end
47+
}
48+
49+
aie.runtime_sequence(%in : memref<16xi32>, %out : memref<16xi32>) {
50+
%c0 = arith.constant 0 : i64
51+
%c1 = arith.constant 1 : i64
52+
%c16 = arith.constant 16 : i64
53+
aiex.npu.dma_memcpy_nd(%out[%c0,%c0,%c0,%c0][%c1,%c1,%c1,%c16][%c0,%c0,%c0,%c1]) {metadata = @of_out, id = 1 : i64} : memref<16xi32>
54+
aiex.npu.dma_memcpy_nd(%in[%c0,%c0,%c0,%c0][%c1,%c1,%c1,%c16][%c0,%c0,%c0,%c1]) {metadata = @of_in, id = 0 : i64, issue_token = true} : memref<16xi32>
55+
aiex.npu.dma_wait {symbol = @of_out}
56+
}
57+
}
58+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===- cpp_link_with_indirect_call.mlir -------------------------*- MLIR -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// (c) Copyright 2026 Advanced Micro Devices, Inc.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
// Test that an indirect call inside a core body triggers a warning from
12+
// aie-assign-core-link-files, since link_with on indirectly-called funcs
13+
// cannot be statically resolved.
14+
15+
// RUN: aie-opt --verify-diagnostics --aie-assign-core-link-files %s
16+
17+
module {
18+
aie.device(npu1_1col) {
19+
%tile_0_2 = aie.tile(0, 2)
20+
21+
func.func private @some_helper() -> ()
22+
23+
%core_0_2 = aie.core(%tile_0_2) {
24+
%fptr = func.constant @some_helper : () -> ()
25+
// expected-warning@+1 {{indirect call in core body}}
26+
func.call_indirect %fptr() : () -> ()
27+
aie.end
28+
}
29+
}
30+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===- cpp_link_with_mixed.mlir --------------------------------*- MLIR -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// Copyright (C) 2026, Advanced Micro Devices, Inc.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
// Test that a core with both a deprecated core-level link_with AND a call to
12+
// a func.func with its own link_with produces a merged, deduplicated link_files
13+
// set. The core-level attr is consumed (removed) and both .o paths appear
14+
// exactly once in link_files.
15+
16+
// RUN: aie-opt --verify-diagnostics --aie-assign-core-link-files %s | FileCheck %s --check-prefix=OPT
17+
// RUN: aie-opt --verify-diagnostics --aie-assign-core-link-files %s | aie-translate --aie-generate-ldscript --tilecol=0 --tilerow=2 | FileCheck %s --check-prefix=LDSCRIPT
18+
19+
// The merged set must contain both files.
20+
// OPT-DAG: "core_only.o"
21+
// OPT-DAG: "func_only.o"
22+
// The deprecated core-level attr must be gone.
23+
// OPT-NOT: link_with = "core_only.o"
24+
25+
// LDSCRIPT-DAG: INPUT(core_only.o)
26+
// LDSCRIPT-DAG: INPUT(func_only.o)
27+
28+
module {
29+
aie.device(npu1_1col) {
30+
%tile_0_0 = aie.tile(0, 0)
31+
%tile_0_2 = aie.tile(0, 2)
32+
33+
aie.objectfifo @of(%tile_0_0, {%tile_0_2}, 2 : i32) : !aie.objectfifo<memref<16xi32>>
34+
35+
func.func private @ext(memref<16xi32>) attributes {link_with = "func_only.o"}
36+
37+
// Core carries deprecated core-level link_with AND calls a func with its own.
38+
// expected-warning@+1 {{link_with on aie.core is deprecated; attach link_with to the func.func declaration instead}}
39+
%core_0_2 = aie.core(%tile_0_2) {
40+
%buf = aie.objectfifo.acquire @of(Consume, 1) : !aie.objectfifosubview<memref<16xi32>>
41+
%elem = aie.objectfifo.subview.access %buf[0] : !aie.objectfifosubview<memref<16xi32>> -> memref<16xi32>
42+
func.call @ext(%elem) : (memref<16xi32>) -> ()
43+
aie.objectfifo.release @of(Consume, 1)
44+
aie.end
45+
} {link_with = "core_only.o"}
46+
47+
aie.runtime_sequence() {}
48+
}
49+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//===- cpp_link_with_shared_func.mlir --------------------------*- MLIR -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// Copyright (C) 2026, Advanced Micro Devices, Inc.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
// Test that two cores each calling the same func.func @kernel {link_with="k.o"}
12+
// each produce exactly one INPUT(k.o) / _include _file k.o entry (no
13+
// duplication of the shared object file).
14+
15+
// RUN: aie-opt --aie-assign-core-link-files %s | FileCheck %s --check-prefix=OPT
16+
// RUN: aie-opt --aie-assign-core-link-files %s | aie-translate --aie-generate-ldscript --tilecol=0 --tilerow=2 | FileCheck %s --check-prefix=LDSCRIPT02
17+
// RUN: aie-opt --aie-assign-core-link-files %s | aie-translate --aie-generate-ldscript --tilecol=0 --tilerow=3 | FileCheck %s --check-prefix=LDSCRIPT03
18+
19+
// OPT-COUNT-2: link_files = ["k.o"]
20+
21+
// LDSCRIPT02: INPUT(k.o)
22+
// LDSCRIPT02-NOT: INPUT(k.o)
23+
24+
// LDSCRIPT03: INPUT(k.o)
25+
// LDSCRIPT03-NOT: INPUT(k.o)
26+
27+
module {
28+
aie.device(npu1_1col) {
29+
%tile_0_0 = aie.tile(0, 0)
30+
%tile_0_2 = aie.tile(0, 2)
31+
%tile_0_3 = aie.tile(0, 3)
32+
33+
// Declare objectfifos before the cores that reference them.
34+
aie.objectfifo @dummy_in(%tile_0_0, {%tile_0_2}, 2 : i32) : !aie.objectfifo<memref<16xi32>>
35+
aie.objectfifo @dummy_in2(%tile_0_0, {%tile_0_3}, 2 : i32) : !aie.objectfifo<memref<16xi32>>
36+
37+
func.func private @kernel(memref<16xi32>) attributes {link_with = "k.o"}
38+
39+
%core_0_2 = aie.core(%tile_0_2) {
40+
%buf = aie.objectfifo.acquire @dummy_in(Consume, 1) : !aie.objectfifosubview<memref<16xi32>>
41+
%elem = aie.objectfifo.subview.access %buf[0] : !aie.objectfifosubview<memref<16xi32>> -> memref<16xi32>
42+
func.call @kernel(%elem) : (memref<16xi32>) -> ()
43+
aie.objectfifo.release @dummy_in(Consume, 1)
44+
aie.end
45+
}
46+
47+
%core_0_3 = aie.core(%tile_0_3) {
48+
%buf = aie.objectfifo.acquire @dummy_in2(Consume, 1) : !aie.objectfifosubview<memref<16xi32>>
49+
%elem = aie.objectfifo.subview.access %buf[0] : !aie.objectfifosubview<memref<16xi32>> -> memref<16xi32>
50+
func.call @kernel(%elem) : (memref<16xi32>) -> ()
51+
aie.objectfifo.release @dummy_in2(Consume, 1)
52+
aie.end
53+
}
54+
55+
aie.runtime_sequence() {}
56+
}
57+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//===- cpp_link_with_unused_func.mlir ---------------------------*- MLIR -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// (c) Copyright 2026 Advanced Micro Devices, Inc.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
// Test that a func.func carrying link_with that is never called from any core
12+
// produces a warning from aie-assign-core-link-files.
13+
14+
// RUN: aie-opt --verify-diagnostics --aie-assign-core-link-files %s
15+
16+
module {
17+
aie.device(npu1_1col) {
18+
%tile_0_2 = aie.tile(0, 2)
19+
20+
// expected-warning@+1 {{func 'never_called' has link_with but is never called from any core; its .o file will not be linked}}
21+
func.func private @never_called(memref<16xi32>) attributes {link_with = "x.o"}
22+
23+
%core_0_2 = aie.core(%tile_0_2) {
24+
aie.end
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)