Source-linked AI summary
OpenMP Meta-Lowering: A Declarative Approach to Performance Portable Parallel Code Generation
Luca Parigi, Giuseppe Tagliavini
TL;DR
Compiler-specific OpenMP lowering limits portability across runtimes, with embedded targets often requiring target-specific lowering work. This paper uses a DSL-driven MLIR pipeline to make lowering runtime-parametric. Across evaluated benchmarks, it matches state-of-the-art compiler performance with code-size overhead below 0.7%.
Problem
Compiler-specific OpenMP lowering is tied to runtime implementations, while supporting embedded targets can require substantial ABI- and execution-model-specific reimplementation.
Source: §1 · p. 2Method
A DSL declares target-specific OpenMP lowering rules that a three-phase MLIR pipeline applies to the program.
Source: §1 · p. 2Results
Across evaluated benchmarks, performance matches state-of-the-art toolchains with code-size overhead below 0.7%.
Source: §1 · p. 2Takeaways & Limitations
Adding support for a new runtime costs a few specification lines, while the lowering pipeline is 5,040 lines for the same OpenMP subset.
Source: §1 · p. 2PMSIS supports fewer constructs because its bare-metal runtime lacks a tasking API, which is needed for task and taskwait.
Source: §3.6 · p. 7
Abstract
from arXiv · showhide
The increasing diversity of parallel hardware challenges existing compilation flows. While OpenMP provides a portable abstraction for shared-memory parallelism, existing compilers tightly couple the frontend semantics with fixed lowering strategies. This design limits performance portability across different runtimes and architectures. In this paper, we present a modular approach to parallel code generation based on the Multi-Level Intermediate Representation (MLIR) framework. Our approach combines an OpenMP frontend with a domain-specific language (DSL) that specifies how OpenMP constructs are lowered for a target. We call this approach OpenMP meta-lowering, treating lowering as a programmable component rather than compiler-specific logic. This design provides explicit control over code outlining, data sharing, and runtime interfacing across diverse targets. We evaluate our approach on PolyBench/C-OMP across general-purpose and embedded multicore targets. Our results show that the proposed method matches the performance of state-of-the-art compiler toolchains with negligible code-size overhead (< 0.7%). Preserving OpenMP constructs until late lowering stages enables both general-purpose and OpenMP-specific MLIR optimizations. By decoupling lowering from compiler internals, our approach expresses the same OpenMP subset in 5,182 lines of code, ~32% fewer than the corresponding lowering code in Clang and ~76% fewer than in GCC, while supporting the pmsis runtime costs only a few specification lines, enabling rapid support for new runtimes without modifying the compiler.
1 Introduction
OpenMP source portability is constrained by compiler lowering strategies tied to particular runtimes, a challenge that is especially costly for embedded targets. The paper makes lowering programmable through a DSL-driven MLIR pipeline and evaluates it across heterogeneous runtimes and targets.
1 Introduction
- OpenMP’s source-level portability does not prevent compiler-specific lowering from embedding runtime interfaces and execution assumptions.Supporting embedded targets can require reimplementing lowering for the target ABI and execution model.Source: §1 · pp. 1–2
- A DSL expresses runtime-specific lowering rules outside compiler logic, making lowering a runtime-agnostic, extensible component.The approach is introduced as a programmable mapping between OpenMP semantics and runtime interfaces.Source: §1 · p. 2
- A three-phase MLIR pipeline annotates the IR, outlines functional contexts, and applies runtime-specific lowering plans.Each phase can be customized through DSL rules.Source: §1 · p. 2
- The Clang/CIR integration permits additional MLIR transformations from early compilation stages, including general-purpose and OpenMP-specific optimizations.These optimizations are also considered in the evaluation.Source: §1 · p. 2
- Across evaluated benchmarks, performance matches state-of-the-art toolchains with code-size overhead below 0.7%.The approach targets libomp, libgomp, and the PULP multicore runtime.Source: §1 · p. 2
- 5,040 lines of lowering code are ∼32% fewer than Clang’s corresponding code and ∼76% fewer than GCC’s for the same OpenMP subset.Adding a runtime costs a few specification lines.Source: §1 · p. 2
2 Background
The background traces OpenMP compilation from high-level MLIR representations to runtime-specific lowering, motivating an external specification in place of compiler-internal runtime logic.
2 Background
- Conventional LLVM/Clang lowering targets libomp, whereas the DSL-driven pipeline supports multiple runtimes and can extend to additional backends.The same OpenMP representation can target multiple runtimes without modifying compiler internals.Source: Figure 1 · p. 3, §2 · p. 3
2.1 MLIR-Based OpenMP Compilation
- MLIR dialects preserve high-level program information as code is progressively lowered, while CIR and the OpenMP dialect keep C/C++ and parallel constructs explicit.This permits transformations before runtime-specific lowering decisions are made.Source: §2.1 · p. 3
2.2 OpenMP Lowering
- OpenMP lowering entails outlining regions, restructuring work-sharing control flow, and emitting runtime calls; current MLIR lowering embeds libomp ABI assumptions.Alternative runtimes therefore require compiler-internal changes rather than a runtime-description change.Source: §2.2 · pp. 3–4
- The proposed response is a declarative specification that names runtime calls and code-generation strategies for the compiler pipeline to apply.The specification externalizes runtime-specific lowering decisions.Source: §2.2 · p. 4
3 Methodology
The methodology specifies how each OpenMP construct is lowered for a target runtime, then applies those rules through a three-pass MLIR pipeline. Its examples show the range of runtime calls and structured rewrites that the DSL can express.
3.1 What a specification can express
- A specification selects the OpenMP operations it handles, exposes clause values, chooses body-handling modes, and invokes structured rewrites.The specification can express constructs reducible to runtime-call sequences associated with the available body-handling modes.Source: §3.1 · p. 4
3.2 Lowering description language
- Runtime declarations bundle construct rules; guards, properties, bindings, and pre/invoke/post blocks describe when and how lowering actions run.Actions are calls to runtime functions or named structured rewrites, and predicates can test construct attributes.Source: §3.2 · pp. 4–5
3.3 MLIR Lowering Pipeline
- Three MLIR passes attach a selected lowering plan, realize its body-handling mode through outlining or loop restructuring, and apply its ordered actions.The plan-application pass emits LLVM calls or structured rewrites before, during, or after the construct.Source: §3.3 · p. 5
3.4 DSL Semantics
- The libgomp example uses DSL properties and actions for parallel dispatch, static or dynamic work-sharing, tasks, and barriers.Static work-sharing computes thread iteration bounds and conditionally emits a barrier unless nowait is present.Source: §3.4 · p. 6
3.5 Well-formedness and semantic preservation
- The parser and lowering checks reject malformed specifications, unmatched constructs, unsupported plan elements, and IR that fails MLIR verification.The passes preserve OpenMP semantics while annotating, outlining, and emitting runtime calls or structured rewrites.Source: §3.5 · p. 7
3.6 Scope and limitations
- PMSIS currently covers fewer constructs because its bare-metal runtime lacks a tasking API; unsupported constructs may require new declarations or emit support.For example, atomic needs an emit keyword for a single-instruction lowering.Source: §3.6 · p. 7
4 Results
Across general-purpose and embedded targets, the evaluation compares OpenMP lowering performance, code size, implementation effort, and pre-lowering MLIR optimizations. Results are reported on PolyBench/C-OMP, using large datasets on the CPU and mini datasets on PULP.
4.1 Experimental setup
- The evaluation uses PolyBench/C-OMP, large datasets on general-purpose platforms, and mini datasets on PULP because of its limited on-chip memory.Benchmarks run with all available hardware threads; tasking is evaluated for correctness on general-purpose runtimes but is unavailable on PULP.Source: §4.1 · pp. 7, 9
4.2 General-purpose runtimes; 4.3 Bare-metal runtime
- Our approach preserves performance across general-purpose benchmarks and is on par with PULP GCC on GAP8.The comparisons use GCC with libgomp and Clang with libomp on the CPU, and PULP GCC on GAP8.Source: Figure 4 · p. 8, Figure 5 · p. 8, §4.2 · p. 9
4.2 General-purpose runtimes
- The doitgen benchmark performs better with our flow under libgomp, where LLVM loop unswitching removes a repeated loop-invariant branch that GCC does not hoist.The branch executes for each (r, q) pair in the large dataset, where NR=150 and NQ=140.Source: §4.2 · p. 9
4.3 Bare-metal runtime
- PULP GCC has higher speedups on floyd-warshall, deriche, and nussinov, where its backend reduces barrier instructions using target-specific knowledge.Incorporating those optimizations into PULP LLVM is outside the paper’s scope.Source: §4.3 · p. 9
- PULP binary-size increases remain below 0.7%, with only a few cases producing slightly larger binaries.The paper treats memory footprint as a critical constraint on ultra-low-power embedded systems.Source: §4.3 · p. 9, Figure 7 · p. 10
4.4 Lowering Complexity and Engineering Cost
- Our full lowering pipeline uses 5,182 lines, compared with minimal lowering implementations of about 7,617 lines for Clang and 21,761 for GCC.The minimal counts cover a specified subset of OpenMP constructs; the approach’s count includes its DSL interpreter and MLIR passes.Source: §4.4 · p. 10, Table 2 · p. 10
4.5 Optimizing Before Lowering: Benefits of MLIR Integration
- CIR loop unrolling by two improves speedup by about 4.6% on average across ten applications, while most other benchmarks are only marginally affected.The improvement ranges from 6.8% for correlation to 1.0% for fdtd-2d.Source: §4.5 · p. 11, Figure 8 · p. 11
- Barrier elision reduces emitted team-barrier calls from 59 to 26, with an overall GAP8 performance saving of 0.037%.Twenty-two of 30 kernels improve; the per-kernel range is −0.16% to +1.12% (trisolv), and regressions are attributed to code layout effects.Source: §4.5 · p. 11
5 Related Work
Prior OpenMP compiler and MLIR approaches either tie lowering to runtime-specific compiler logic or focus on preserving parallel structure and applying transformations. This work instead makes runtime mapping configurable through a declarative lowering specification without modifying compiler internals.
OpenMP compiler and runtime implementations
- Mainstream GCC and LLVM toolchains couple OpenMP lowering to compiler-specific code paths and runtime ABIs, a constraint heightened by specialized embedded runtimes.Embedded OpenMP implementations include custom approaches and compiler-assisted adaptive runtime organization.Source: §5 · p. 11
Decoupling lowering from compiler and runtime
- Mercurium uses source-language code templates, ROSE targets XOMP, and OpenUH can re-emit lowered code as source; this approach instead directly invokes the target runtime.Its rules apply to OpenMP constructs rather than instantiating templates or translating through a common library, and IR transformations can precede lowering.Source: §5 · pp. 11–12
Intermediate representations for parallel semantics
- Tapir, OpenMP-aware optimizations, and UPIR preserve parallel semantics for optimization or analysis, but do not make lowering itself programmable.These approaches are cited as,, and, respectively.Source: §5 · p. 12
MLIR-based OpenMP compilation and declarative specification
- MLIR represents OpenMP in its omp dialect and supports declarative rewrites and scripted optimizations, but its final lowering remains tied to a runtime ABI.DRR, PDLL, and the Transform dialect lack OpenMP-lowering concepts such as outlining and capture-struct layout; other multi-level IR flows also leave lowering in passes or pipelines.Source: §5 · p. 12
Imperative and structure-preserving alternatives
- The OpenMPIRBuilder factors runtime-call emission into helpers but fixes the target runtime, while this DSL specifies the mapping declaratively without compiler-internal changes.Polygeist preserves high-level structure for loop optimization, but its lowering remains fixed and compiler-internal.Source: §5 · p. 12
Positioning of this work
- This work elevates OpenMP lowering to a first-class, runtime-agnostic abstraction, reconfiguring the same representation for different runtimes without compiler-internal modifications.It differs from prior work focused on runtime specialization, preserving parallel semantics, or structured compilation infrastructures.Source: §5 · p. 12
6 Conclusion
The paper presents OpenMP lowering as a programmable, runtime-agnostic process specified through an external DSL. It reports performance and code-size results without added cost, a 5,182-line implementation smaller than the corresponding Clang and GCC code, and identifies the current OpenMP subset as a boundary.
6 Conclusion
- 5,182 lines implement the lowering pipeline—∼32% fewer than corresponding Clang code and ∼76% fewer than GCC code.The evaluation reports this flexibility comes at no cost in performance or code size.Source: §6 · p. 12
- The approach treats lowering as a programmable, runtime-agnostic process through a configurable external DSL specification.The paper contrasts this with traditional lowering encoded in compiler passes.Source: §6 · p. 12
- The current implementation supports an OpenMP subset; offloading and more complex synchronization patterns remain future extensions.The paper describes the supported subset as sufficient for different application domains.Source: §6 · p. 12
A.1 Abstract
The artifact accompanies the paper submitted to CGO 2027 and supports reproduction of the results in Section 4.
A.1 Abstract
- The artifact supports the paper titled “OpenMP Meta-Lowering: A Declarative Approach to Performance Portable Parallel Code Generation,” submitted to CGO 2027.It is described as the paper's supporting artifact.Source: §A.1 · p. 13
- The artifact can reproduce all results presented in Section 4.Source: §A.1 · p. 13
- The artifact is associated with a CGO 2027 submission.Source: §A.1 · p. 13
A.2 Artifact check-list
The checklist specifies the implementation, benchmarks, compilation workflow, platforms, metrics, and outputs used in the experiments. It also reports the resources and time needed to reproduce them and gives availability and licensing information.
A.2 Artifact check-list
- The artifact uses declarative runtime-parametric OpenMP lowering through a DSL and three MLIR passes, with an optional redundant team-barrier elimination pass.Its passes annotate, outline, and apply a lowering plan to omp.* operations.Source: §A.2 · p. 13
- The program set comprises PolyBench/C 4.2.1 OpenMP kernels and hand-written MLIR modules, using LARGE_DATASET on general-purpose platforms and MINI_DATASET on GAP8.Source: §A.2 · p. 13
- The workflow includes LLVM/MLIR 23.0.0 with ClangIR, GCC 12.3.0, GAP8 GCC 7.1, and PULP LLVM 18.1.4 toolchains.It uses host ELF binaries for general-purpose experiments and bare-metal RISC-V ELF binaries for GAP8.Source: §A.2 · p. 13
- The checklist reports speedup, binary size, emitted team-barrier counts, and lines of code, with execution logs, CSV files, plots, and tables as outputs.GAP8 runs use the cycle-accurate gvsoc simulator; other general-purpose loads can affect host timings.Source: §A.2 · p. 13
- The Docker image requires 20.6 GB, preparation takes about 15 minutes, and full general-purpose performance runs take about 5 hours.Reducing timing repetitions to REPS=1 brings the complete artifact under one hour but removes run-to-run variation measurement.Source: §A.2 · p. 13
- The artifact is publicly available under Apache License 2.0 with LLVM Exceptions; its data uses the PolyBench/C 4.2.1 and OpenMP-variant license.The artifact is archived at the listed Zenodo DOI.Source: §A.2 · p. 13
A.3 Description
The artifact is distributed as a Docker container with toolchains, benchmarks, and scripts for reproducing results. Reproduction requires a Linux x86-64 host, Docker, and sufficient free disk space, with host core count affecting observed speedups.
A.3.1 How delivered
- The archived Docker image contains the toolchains, benchmarks, and scripts needed to reproduce the results.The artifact is available from Zenodo.Source: §A.3.1 · p. 13
A.3.2 Hardware dependencies
- Reproducing the published general-purpose speedups in Figures 4 and 5 requires a host with a comparable core count; comparison shape is preserved on any multicore host.GAP8 experiments are simulated and have no hardware dependency.Source: §A.3.2 · p. 13
- The artifact requires Linux x86-64, at least 20.6 GB of free disk space, and a working Docker installation.Internet access is needed only to download the image and count lines of code for upstream GCC and LLVM versions.Source: §A.3.2 · p. 13, §A.3.3 · p. 13
A.4 Installation
Installation instructions load the archived Docker image, start a container, and check that the bundled compilers are available.
A.4 Installation
- Load the downloaded artifact with Docker using the supplied image archive, then start the mlir-opt-omp:latest container to verify it runs.The archive is named mlir-opt-omp-1.0.0-linux-amd64.docker.tar.zst.Source: §A.4 · p. 13
- The installation instructions check the bundled GCC 12 and LLVM-MLIR Clang compiler versions inside the container.Source: §A.4 · p. 14
- The container also includes the PULP LLVM Clang and RISC-V GCC toolchains, whose versions can be checked with the supplied commands.Source: §A.4 · p. 14
A.5 Experiment workflow
The appendix documents scripts for tests, correctness checks, performance figures, and line counts, then specifies expected outcomes, customization options, and implementation notes.
A.5 Experiment workflow
- The workflow runs 74 lit tests, bit-by-bit correctness checks for 30 kernels per runtime, and twelve construct tests covering constructs absent from PolyBench.Correctness checks run with iomp, libgomp, and pmsis; construct tests run with iomp and libgomp.Source: §A.5 · p. 14
- The first two performance configurations generate Figures 4 and 5 in about five hours together, while the third generates Figures 6 and 7; the unroll script generates Figure 8.A comparison reports cycles with and without barrier elimination.Source: §A.5 · p. 14
- The line-count workflow clones GCC and LLVM at pinned commits before running scripts for Table 2.Source: §A.5 · p. 14
A.6 Evaluation and expected result
- Expected checks include passing tests and kernels, reported line counts and barrier counts, and deterministic GAP8 simulations that should closely replicate Figures 6–8.Host speedups scale with the machine, so evaluation uses per-kernel ratios against the native toolchain and their suite geometric mean rather than absolute speedups.Source: §A.6 · p. 14
A.7 Experiment customization
- Scripts accept runtime, dataset, thread, kernel, repetition, barrier-elimination, plotting, and output-directory settings; reducing repetitions shortens runs.Source: §A.7 · p. 14
A.8 Notes
- Vectorization-off and precise floating-point settings enable bit-exact correctness checks; contraction and reassociation can make iterative-kernel binaries diverge.Source: §A.8 · p. 14
- The DSL uses branch blocks for runtime-known if clauses and first_chunk/next_chunk blocks for dynamic scheduling; outlined function signatures follow capture_strategy.The paper's libomp runtime is called iomp in the artifact, and outline_signature was removed as redundant.Source: §A.8 · p. 14