Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,12 @@ endif()

add_compile_options(-Wall -Wextra)

# LLVM/MLIR is prebuilt with RTTI and exceptions disabled (see
# extern/llvm-project/bin/llvm-config --cxxflags). Compile project code with
# the same settings so the CoIR tools link against the prebuilt bitcode
# libraries without typeinfo/exception symbol mismatches.
add_compile_options(-fno-rtti -fno-exceptions)

if(EMSCRIPTEN)
# Choreo is developed against GCC; suppress Clang-specific pedantic warnings
# that do not indicate actual bugs when cross-compiling to WASM.
Expand Down Expand Up @@ -544,7 +550,9 @@ include(${CMAKE_SOURCE_DIR}/cmake/FileCheckBootstrap.cmake)
if(NOT EMSCRIPTEN)

set(EXTERN_LIBS
stdc++fs
# stdc++fs was previously required for GCC/libstdc++'s std::filesystem.
# With the clang++/libc++ toolchain, std::filesystem is provided by libc++
# itself, and libstdc++fs is neither available nor ABI-compatible.
)

if(ENABLE_CUDA)
Expand Down Expand Up @@ -572,6 +580,10 @@ if(CHOREO_BUILD_CHOREO)
add_dependencies(choreo parser_deps)
add_dependencies(copp parser_deps)

# choreo_main.cpp drives the bison-generated parser directly (Scanner/Parser),
# which requires RTTI + exceptions; the rest of the target stays -fno-rtti.
target_compile_options(choreo PRIVATE -frtti -fexceptions)

target_link_libraries(choreo PRIVATE
parse core codegen support pp
-Wl,--whole-archive targets -Wl,--no-whole-archive
Expand Down
22 changes: 19 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ SHELL:=/bin/bash

WORK_DIR:=$(CURDIR)
TOOLCHAIN_DIR=$(WORK_DIR)/extern
LLVM_BIN_DIR=$(TOOLCHAIN_DIR)/llvm-project/bin
LLVM_CC=$(LLVM_BIN_DIR)/clang
LLVM_CXX=$(LLVM_BIN_DIR)/clang++
LLVM_LIBCXX_DIR=$(TOOLCHAIN_DIR)/llvm-project/lib/x86_64-unknown-linux-gnu
TOOLS_DIR=$(WORK_DIR)/tools
SCRIPT_DIR=$(WORK_DIR)/scripts
RT_DIR=$(WORK_DIR)/runtime
Expand Down Expand Up @@ -107,9 +111,13 @@ symlink-coir:

define build-croqtile
@echo "=== Building CoIR tools ($(1)) ==="
$(CMAKE) -S $(WORK_DIR) -B $(2) \
PATH=$(LLVM_BIN_DIR):$$PATH $(CMAKE) -S $(WORK_DIR) -B $(2) \
-G Ninja \
-DCMAKE_BUILD_TYPE=$(1) \
-DCMAKE_C_COMPILER=$(LLVM_CC) \
-DCMAKE_CXX_COMPILER=$(LLVM_CXX) \
'-DCMAKE_CXX_FLAGS=-stdlib=libc++' \
'-DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++ -fuse-ld=lld -Wl,-rpath,$(LLVM_LIBCXX_DIR)' \
-DCHOREO_DEFAULT_TARGET=$(CHOREO_DEFAULT_TARGET) \
'-DCROQ_PROJECT=$(CROQ_PROJECT)' \
'-DCROQ_TARGET=$(CROQ_TARGET)'
Expand Down Expand Up @@ -153,9 +161,13 @@ endef
# ---- coir-only (no CoIR) ----
define build-coir-only
@echo "=== Building CoIR tools ($(1)) ==="
$(CMAKE) -S $(WORK_DIR) -B $(2) \
PATH=$(LLVM_BIN_DIR):$$PATH $(CMAKE) -S $(WORK_DIR) -B $(2) \
-G Ninja \
-DCMAKE_BUILD_TYPE=$(1) \
-DCMAKE_C_COMPILER=$(LLVM_CC) \
-DCMAKE_CXX_COMPILER=$(LLVM_CXX) \
'-DCMAKE_CXX_FLAGS=-stdlib=libc++' \
'-DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++ -fuse-ld=lld -Wl,-rpath,$(LLVM_LIBCXX_DIR)' \
-DCHOREO_DEFAULT_TARGET=$(CHOREO_DEFAULT_TARGET) \
'-DCROQ_PROJECT=coir' \
'-DCROQ_TARGET=$(CROQ_TARGET)'
Expand Down Expand Up @@ -263,8 +275,12 @@ CROQ_TARGET ?= all
build-with-cmake-ninja:
@echo "Starting build with CMake..."
@if [ ! -d $(CMAKE_BUILD_DIR) ]; then mkdir -p $(CMAKE_BUILD_DIR); fi
$(CMAKE) -S . -B $(CMAKE_BUILD_DIR) -G Ninja \
PATH=$(LLVM_BIN_DIR):$$PATH $(CMAKE) -S . -B $(CMAKE_BUILD_DIR) -G Ninja \
-DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) \
-DCMAKE_C_COMPILER=$(LLVM_CC) \
-DCMAKE_CXX_COMPILER=$(LLVM_CXX) \
'-DCMAKE_CXX_FLAGS=-stdlib=libc++' \
'-DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++ -fuse-ld=lld -Wl,-rpath,$(LLVM_LIBCXX_DIR)' \
-DPUBLIC_PACKAGE=$(PUBLIC_PACKAGE) \
-DSTANDALONE=$(STANDALONE) \
-DCHOREO_DEFAULT_TARGET=$(CHOREO_DEFAULT_TARGET) \
Expand Down
9 changes: 8 additions & 1 deletion lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ add_custom_target(
set(PARSER_SOURCES
${CMAKE_BINARY_DIR}/parser.tab.cc
${CMAKE_BINARY_DIR}/scanner.yy.cc
# choreo_api.cpp hosts CompilerAPI::Parse, which drives the bison parser.
# choreo_api.cpp hosts CompilerAPI::Parse, which drives the bison parser and
# therefore needs RTTI + exceptions (compiled as part of the parse library).
choreo_api.cpp
)

Expand Down Expand Up @@ -106,6 +107,12 @@ set(PP_SOURCES

# Add the libraries
add_library(parse OBJECT ${PARSER_SOURCES})
# Bison's variant-based semantic values (api.value.type variant) use typeid for
# runtime type checking, and its error recovery uses try/catch, so the generated
# parser needs RTTI and exceptions even though the rest of the project compiles
# with -fno-rtti -fno-exceptions. This only affects the auto-generated
# parser.tab.cc / scanner.yy.cc translation units.
target_compile_options(parse PRIVATE -frtti -fexceptions)
add_library(core OBJECT ${CORE_SOURCES})
add_library(codegen OBJECT ${CODEGEN_SOURCES})
add_library(support OBJECT ${CL_SOURCES})
Expand Down
19 changes: 19 additions & 0 deletions lib/Target/GPU/cute_target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,25 @@ ArchId NVGPUTarget::ResolveNativeArch() const {
return "";
}

FenceSelection NVGPUTarget::SelectDMAFences(const ArchId& arch, Storage src,
Storage dst) const {
(void)arch; // B1 table is arch-invariant; gate on sm_90+ only if regressions.
FenceSelection sel;
if (src == Storage::GLOBAL && dst == Storage::SHARED) {
// Producer: threads release global before the DMA engine reads it.
sel.producer.push_back(
FenceKind{Storage::GLOBAL, FenceEntity::THREADS, FenceOrder::RELEASE});
// Consumer: threads acquire shared after the DMA engine writes it.
sel.consumer.push_back(
FenceKind{Storage::SHARED, FenceEntity::THREADS, FenceOrder::ACQUIRE});
} else if (src == Storage::SHARED && dst == Storage::GLOBAL) {
// S2G store: threads release shared before the engine reads it.
sel.producer.push_back(
FenceKind{Storage::SHARED, FenceEntity::THREADS, FenceOrder::RELEASE});
}
return sel;
}

namespace {

class CuteTarget : public NVGPUTarget {
Expand Down
7 changes: 7 additions & 0 deletions lib/Target/GPU/gpu_target.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,13 @@ class NVGPUTarget : public GPUTarget {
// default fence order is seq_cst and `sync.fence.seq_cst` is accepted.
bool SupportsSeqCstFence(const ArchId&) const override { return true; }

// Fence requirements for DMA edges (B1 conservative table, storage-direction
// keyed). A global->shared edge releases global at the producer and acquires
// shared at the consumer; a shared->global edge releases shared at the
// producer. See the MARA fence-placement plan (Phase B1).
FenceSelection SelectDMAFences(const ArchId& arch, Storage src,
Storage dst) const override;

ArchId ResolveNativeArch() const override;
};

Expand Down
5 changes: 3 additions & 2 deletions lib/command_line.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ Option<bool>
"Utilize native bf16 type when target platform support.");

Option<bool> insert_dma_fences(
OptionKind::User, "--insert-dma-fences", "", true,
OptionKind::User, "--insert-dma-fences", "", false,
"Insert memory fences between DMA producer and consumer sites "
"(default: true). Disable with --insert-dma-fences=false.");
"(default: false; experimental, not yet minimal). Enable with "
"--insert-dma-fences=true.");

Option<bool> dump_fence_insertion(
OptionKind::User, "--dump-fence-insertion", "", false,
Expand Down
7 changes: 5 additions & 2 deletions lib/fence_insertion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@ bool FenceInsertion::Visit(AST::DMA& n) {
const BufferAccessEvent* dst_ev = nullptr;
if (auto it = dma_dst_.find(&n); it != dma_dst_.end()) dst_ev = it->second;

const Storage src_storage = src_ev->storage;
const Storage dst_storage = dst_ev ? dst_ev->storage : Storage::NONE;
// Unannotated buffers default to global storage; normalize so the target's
// storage-directional table sees canonical GLOBAL (mirrors dma_plan.cpp).
const Storage src_storage = ProjectStorage(src_ev->storage);
const Storage dst_storage =
dst_ev ? ProjectStorage(dst_ev->storage) : Storage::NONE;

FenceSelection sel = CCtx().GetTarget().SelectDMAFences(
CCtx().GetArch(), src_storage, dst_storage);
Expand Down
12 changes: 6 additions & 6 deletions lib/pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,14 @@ ASTPipeline& ASTPipeline::PlanSemanticRoutine() {
// so the checker can validate events with thread count info)
AddStage<ActiveThreadsAnalysis>();

// record the ordered (READ | WRITE) buffer access log consumed by
// FenceInsertion during codegen.
// record the ordered (READ | WRITE) buffer access log.
AddStage<BufferAccessAnalyzer>();

// compute and annotate DMA producer/consumer fences from the buffer access
// log. Runs in the semantic routine (not codegen) so that co2ir's
// RunFrontend path (NoCodegen=true) still gets the fence annotations.
AddStage<FenceInsertion>();

// apply the semantic check
AddStage<SemaChecker>();
return *this;
Expand All @@ -330,10 +334,6 @@ ASTPipeline& ASTPipeline::PlanCodeGenRoutine() {

AddStage<CodegenPrepare>();

// compute and annotate DMA producer/consumer fences from the buffer access
// log recorded during the semantic routine.
AddStage<FenceInsertion>();

// delegate to target for codegen plan
if (!CCtx().GetTarget().PlanCodeGenStages(*this)) {
errs() << "Failed to initialize codegen for target '" << CCtx().TargetName()
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/insert_dma_fences.co
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ __co__ void foo() {
}

// HELP: --insert-dma-fences
// HELP: default: true
// HELP: default: false
// BADVAL: Invalid value for boolean option '--insert-dma-fences'
4 changes: 2 additions & 2 deletions tools/coir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ target_link_libraries(co2ir PRIVATE
-Wl,--whole-archive targets -Wl,--no-whole-archive
MLIRCoIRDialect MLIRCoIRTransforms MLIRCoIRDriver
${COIR_MLIR_LIBS}
stdc++fs Threads::Threads
Threads::Threads
)
# Link codegen libs for emission support
foreach(_lib ${COIR_CODEGEN_LIBS})
Expand Down Expand Up @@ -283,7 +283,7 @@ target_link_libraries(cocc PRIVATE
-Wl,--whole-archive targets -Wl,--no-whole-archive
MLIRCoIRDialect MLIRCoIRTransforms MLIRCoIRDriver
${COIR_MLIR_LIBS}
stdc++fs Threads::Threads
Threads::Threads
)
# Link all codegen libs (whole-archive for static registrations)
foreach(_lib ${COIR_CODEGEN_LIBS})
Expand Down
11 changes: 8 additions & 3 deletions tools/coir/lib/ASTIRGen/ASTCoIRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4337,17 +4337,22 @@ void ASTCoIRGen::emitFenceKinds(llvm::StringRef joined, mlir::Location loc) {
if (kinds.empty()) return;

// Emit one coir.fence per four-fold kind, carrying every axis
// (space, entity, order, scope) explicitly. Engine/proxy fences keep the
// auto scope (NONE), which codegen derives from `space`.
// (space, entity, order, scope) explicitly. An AUTO scope (NONE) resolves to
// the space's natural coherence level (GLOBAL -> DEVICE, SHARED -> GROUP,
// ...), mirroring the explicit sync.fence path in the parser. The backend
// (EmitCUDA/EmitHIP) requires a concrete scope, so resolve before lowering.
for (const auto& kind : kinds) {
if (kind.IsNone()) continue;
ParallelLevel scope = kind.scope;
if (scope == ParallelLevel::NONE)
scope = DefaultLevelForStorage(kind.space);
builder.create<coir::FenceOp>(
loc,
coir::TensorMemorySpaceAttr::get(&IRContext(),
lowerFenceSpace(kind.space)),
coir::FenceEntityAttr::get(&IRContext(), lowerFenceEntity(kind.entity)),
coir::FenceOrderAttr::get(&IRContext(), lowerFenceOrder(kind.order)),
LowerParallelLevel(kind.scope));
LowerParallelLevel(scope));
}
}

Expand Down
40 changes: 40 additions & 0 deletions tools/coir/tests/irgen/dma-fence.co
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// RUN: co2ir --insert-dma-fences=true %s | FileCheck %s

// B1: DMA edges emit coir.fence ops via the NVGPUTarget::SelectDMAFences
// override. A GLOBAL -> SHARED copy emits a producer GLOBAL release (before
// the copy) and a consumer SHARED acquire (after the wait, before threads
// read the shared data). A SHARED -> GLOBAL copy after a thread write emits
// a producer SHARED release. AUTO scope resolves from the space (GLOBAL ->
// device, SHARED -> group).

__co__ auto g2s_read(f32 [1024] input) {
f32 [input.span(0)] output;
parallel p by 1 {
fa = dma.copy.async input.chunkat(p) => shared;
wait fa;
foreach idx in [1024] {
output.at(idx) = fa.data.at(idx);
}
}
return output;
}
// CHECK: coir.kernel @g2s_read
// CHECK: coir.fence <global> <threads> <release> <device>
// CHECK: coir.dma.copy
// CHECK: coir.wait
// CHECK: coir.fence <shared> <threads> <acquire> <group>

__co__ auto s2g_write(f32 [1024] input) {
f32 [input.span(0)] output;
parallel p by 1 {
shared f32 [1024] sbuf;
sbuf.at(0) = 1.0;
dma.copy sbuf => output.chunkat(p);
}
return output;
}
// CHECK: coir.kernel @s2g_write
// CHECK: coir.fence <shared> <threads> <release> <group>
// CHECK: coir.dma.copy
// CHECK: coir.wait
// CHECK: coir.return