From 7eff0a5056fcd233d71eae434c1c8f9962c4a57e Mon Sep 17 00:00:00 2001 From: "Mei, Yijie" Date: Fri, 10 May 2024 12:57:30 +0800 Subject: [PATCH 1/4] add for --- include/gc/IR/EasyBuild.h | 8 +- include/gc/IR/EasyBuildSCF.h | 112 ++++++++++++++++++++++++ unittests/Dialect/CMakeLists.txt | 3 +- unittests/Dialect/SCF/CMakeLists.txt | 7 ++ unittests/Dialect/SCF/EasyBuildTest.cpp | 70 +++++++++++++++ 5 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 include/gc/IR/EasyBuildSCF.h create mode 100644 unittests/Dialect/SCF/CMakeLists.txt create mode 100644 unittests/Dialect/SCF/EasyBuildTest.cpp diff --git a/include/gc/IR/EasyBuild.h b/include/gc/IR/EasyBuild.h index da3da952d..0c9dc23e8 100644 --- a/include/gc/IR/EasyBuild.h +++ b/include/gc/IR/EasyBuild.h @@ -84,8 +84,12 @@ struct EasyBuilder { template auto F(Args &&...v) { - return wrap( - builder->builder.create(builder->loc, std::forward(v)...)); + if constexpr (std::is_same_v) { + builder->builder.create(builder->loc, std::forward(v)...); + } else { + return wrap( + builder->builder.create(builder->loc, std::forward(v)...)); + } } }; diff --git a/include/gc/IR/EasyBuildSCF.h b/include/gc/IR/EasyBuildSCF.h new file mode 100644 index 000000000..c765e4818 --- /dev/null +++ b/include/gc/IR/EasyBuildSCF.h @@ -0,0 +1,112 @@ +//===- EasyBuildSCF.h - Easy IR Builder for general control flow *- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header file defines the helper classes, functions and macros to help to +// build general structured control flow. Developers can use the utilities in +// this header to easily compose control flow IR. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_IR_EASYBUILDSCF_H +#define MLIR_IR_EASYBUILDSCF_H +#include "gc/IR/EasyBuild.h" +#include "mlir/Interfaces/LoopLikeInterface.h" + +namespace mlir { +namespace easybuild { +namespace impl { + +struct ForRangeSimulatorImpl { + StatePtr s; + LoopLikeOpInterface op; + ForRangeSimulatorImpl(const StatePtr &s, LoopLikeOpInterface op) + : s{s}, op{op} { + s->builder.setInsertionPointToStart(&op.getLoopRegions().front()->front()); + } + ~ForRangeSimulatorImpl() { + s->builder.setInsertionPointAfter(op); + } +}; + +template +using NthTypeOf = typename std::tuple_element>::type; + +template struct ForVarBinding { + ForRangeSimulatorImpl *impl; + template auto get() { + using TOut = NthTypeOf; + if (auto wrapped = TOut::wrapOrFail( + impl->s, impl->op.getLoopRegions().front()->front().getArgument(I)); + succeeded(wrapped)) { + return *wrapped; + } + llvm_unreachable("Bad cast for the loop iterator"); + } +}; +} // namespace impl +} // namespace easybuild +} // namespace mlir + +namespace std { +template +struct tuple_size> + : std::integral_constant {}; + +template +struct tuple_element> { + using type = mlir::easybuild::impl::NthTypeOf; +}; +} // namespace std + +namespace mlir { +namespace easybuild { + +namespace impl { + +template struct ForRangeSimulator : ForRangeSimulatorImpl { + using ForRangeSimulatorImpl::ForRangeSimulatorImpl; + struct ForRangeIterator { + ForRangeSimulatorImpl *ptr; + bool consumed; + auto operator*() const { return ForVarBinding{ptr}; } + + ForRangeIterator &operator++() { + consumed = true; + return *this; + } + + bool operator!=(ForRangeIterator &other) const { + return consumed != other.consumed; + } + + ForRangeIterator(ForRangeSimulator *ptr) + : ptr{ptr}, consumed{false} {} + ForRangeIterator() : ptr{nullptr}, consumed{true} {} + }; + + ForRangeIterator begin() { return ForRangeIterator(this); } + + ForRangeIterator end() { return ForRangeIterator(); } +}; +} // namespace impl + +template +auto forRangeIn(const impl::StatePtr &s, LoopLikeOpInterface op) { + return impl::ForRangeSimulator{s, op}; +} + +template +auto forRangeIn(const EasyBuilder &s, LoopLikeOpInterface op) { + return impl::ForRangeSimulator{s.builder, op}; +} + +#define EB_for for + +} // namespace easybuild +} // namespace mlir +#endif diff --git a/unittests/Dialect/CMakeLists.txt b/unittests/Dialect/CMakeLists.txt index 42445996f..a5e8e7102 100644 --- a/unittests/Dialect/CMakeLists.txt +++ b/unittests/Dialect/CMakeLists.txt @@ -1 +1,2 @@ -add_subdirectory(Arith) \ No newline at end of file +add_subdirectory(Arith) +add_subdirectory(SCF) \ No newline at end of file diff --git a/unittests/Dialect/SCF/CMakeLists.txt b/unittests/Dialect/SCF/CMakeLists.txt new file mode 100644 index 000000000..e7413d9d4 --- /dev/null +++ b/unittests/Dialect/SCF/CMakeLists.txt @@ -0,0 +1,7 @@ +add_mlir_unittest(MLIRSCFTests + EasyBuildTest.cpp) +target_link_libraries(MLIRSCFTests + PRIVATE + MLIRFuncDialect + MLIRSCFDialect + MLIRArithDialect) diff --git a/unittests/Dialect/SCF/EasyBuildTest.cpp b/unittests/Dialect/SCF/EasyBuildTest.cpp new file mode 100644 index 000000000..475216b76 --- /dev/null +++ b/unittests/Dialect/SCF/EasyBuildTest.cpp @@ -0,0 +1,70 @@ +//===- EasyBuildTest.cpp - Tests SCF Op Easy builders ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gc/Dialect/Arith/Utils/EasyBuild.h" +#include "gc/IR/EasyBuildSCF.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Dialect/SCF/IR/SCF.h" +#include "gtest/gtest.h" + +using namespace mlir; +using namespace mlir::easybuild; + +namespace { +class SCFTest : public ::testing::Test { +protected: + SCFTest() { + context.getOrLoadDialect(); + context.getOrLoadDialect(); + } + + mlir::MLIRContext context; +}; +} // namespace + +TEST_F(SCFTest, EasyBuild) { + OpBuilder builder{&context}; + auto loc = builder.getUnknownLoc(); + EasyBuilder b{builder, loc, true}; + auto func = builder.create( + loc, "funcname", + FunctionType::get(&context, {builder.getIndexType()}, + {builder.getIndexType()})); + + builder.setInsertionPointToStart(func.addEntryBlock()); + auto init_val = b.wrap(func.getArgument(0)) + b.toIndex(10); + auto loop = builder.create(loc, /*lo*/ b.toIndex(0), + /*upper*/ b.toIndex(10), + /*step*/ b.toIndex(1), + /*ind_var*/ ValueRange{init_val}); + EB_for(auto &&[idx, redu] : forRangeIn(b, loop)) { + auto idx2 = idx + b.toIndex(1); + b.F(ValueRange{idx2 + redu}); + } + builder.create(loc, loop.getResult(0)); + std::string out; + llvm::raw_string_ostream os{out}; + os << func; + + const char *expected = + R"mlir(func.func @funcname(%arg0: index) -> index { + %c10 = arith.constant 10 : index + %0 = arith.addi %arg0, %c10 : index + %c1 = arith.constant 1 : index + %c10_0 = arith.constant 10 : index + %c0 = arith.constant 0 : index + %1 = scf.for %arg1 = %c0 to %c10_0 step %c1 iter_args(%arg2 = %0) -> (index) { + %c1_1 = arith.constant 1 : index + %2 = arith.addi %arg1, %c1_1 : index + %3 = arith.addi %2, %arg2 : index + scf.yield %3 : index + } + return %1 : index +})mlir"; + ASSERT_EQ(out, expected); +} From 24e36519f9da0501c4d46b7b15a34b948de4910b Mon Sep 17 00:00:00 2001 From: "Mei, Yijie" Date: Fri, 10 May 2024 16:23:42 +0800 Subject: [PATCH 2/4] finish if --- include/gc/IR/EasyBuild.h | 14 +++++ include/gc/IR/EasyBuildSCF.h | 84 +++++++++++++++++++++++-- unittests/Dialect/SCF/EasyBuildTest.cpp | 50 +++++++++++++-- 3 files changed, 139 insertions(+), 9 deletions(-) diff --git a/include/gc/IR/EasyBuild.h b/include/gc/IR/EasyBuild.h index 0c9dc23e8..0c0275138 100644 --- a/include/gc/IR/EasyBuild.h +++ b/include/gc/IR/EasyBuild.h @@ -19,6 +19,10 @@ #include namespace mlir { +namespace scf { +class YieldOp; +} + namespace easybuild { namespace impl { @@ -58,6 +62,10 @@ struct EasyBuilder { : builder{builder} {} void setLoc(const Location &l) { builder->loc = l; } + Operation *getLastOperaion() { + return &*(--builder->builder.getInsertionPoint()); + } + template auto wrapOrFail(V &&v) { return W::wrapOrFail(builder, std::forward(v)); } @@ -91,6 +99,12 @@ struct EasyBuilder { builder->builder.create(builder->loc, std::forward(v)...)); } } + + template + auto yield(Args &&...v) { + builder->builder.create(builder->loc, + ValueRange{std::forward(v)...}); + } }; } // namespace easybuild diff --git a/include/gc/IR/EasyBuildSCF.h b/include/gc/IR/EasyBuildSCF.h index c765e4818..4f29728f0 100644 --- a/include/gc/IR/EasyBuildSCF.h +++ b/include/gc/IR/EasyBuildSCF.h @@ -18,6 +18,10 @@ #include "mlir/Interfaces/LoopLikeInterface.h" namespace mlir { +namespace scf { +class IfOp; +} + namespace easybuild { namespace impl { @@ -28,9 +32,7 @@ struct ForRangeSimulatorImpl { : s{s}, op{op} { s->builder.setInsertionPointToStart(&op.getLoopRegions().front()->front()); } - ~ForRangeSimulatorImpl() { - s->builder.setInsertionPointAfter(op); - } + ~ForRangeSimulatorImpl() { s->builder.setInsertionPointAfter(op); } }; template @@ -84,8 +86,7 @@ template struct ForRangeSimulator : ForRangeSimulatorImpl { return consumed != other.consumed; } - ForRangeIterator(ForRangeSimulator *ptr) - : ptr{ptr}, consumed{false} {} + ForRangeIterator(ForRangeSimulator *ptr) : ptr{ptr}, consumed{false} {} ForRangeIterator() : ptr{nullptr}, consumed{true} {} }; @@ -107,6 +108,79 @@ auto forRangeIn(const EasyBuilder &s, LoopLikeOpInterface op) { #define EB_for for +namespace impl { +struct IfSimulator; +struct IfIterator { + IfSimulator *ptr; + int index; + int operator*() const; + + IfIterator &operator++() { + index++; + return *this; + } + + bool operator!=(IfIterator &other) const { return index != other.index; } + + IfIterator(IfSimulator *ptr) : ptr{ptr}, index{0} {} + IfIterator(int numRegions) : ptr{nullptr}, index{numRegions} {} +}; + +struct IfSimulator { + StatePtr s; + Operation *op; + IfIterator begin() { return IfIterator(this); } + IfIterator end() { + int nonEmptyRegions = 0; + for (auto ® : op->getRegions()) { + if (reg.begin() != reg.end()) { + nonEmptyRegions++; + } + } + return IfIterator(nonEmptyRegions); + } + ~IfSimulator() { s->builder.setInsertionPointAfter(op); } +}; +inline int IfIterator::operator*() const { + auto &blocks = ptr->op->getRegion(index); + ptr->s->builder.setInsertionPointToStart(&blocks.back()); + return index; +} + +} // namespace impl + +impl::IfSimulator makeIfRange(const EasyBuilder &s, Operation *op) { + return impl::IfSimulator{s.builder, op}; +} + +template +impl::IfSimulator makeScfIfLikeRange(EBValue cond, TypeRange resultTypes) { + auto &s = cond.builder; + auto op = s->builder.create(s->loc, resultTypes, cond, true); + return impl::IfSimulator{s, op}; +} + +template +impl::IfSimulator makeScfIfLikeRange(EBValue cond, bool hasElse = true) { + auto &s = cond.builder; + auto op = s->builder.create(s->loc, TypeRange{}, cond, hasElse); + return impl::IfSimulator{s, op}; +} + +#define EB_if(BUILDER, ...) \ + for (auto &&eb_mlir_if_scope__ : \ + ::mlir::easybuild::makeIfRange(BUILDER, __VA_ARGS__)) \ + if (eb_mlir_if_scope__ == 0) + +// EB_scf_if(COND) +// EB_scf_if(COND, HAS_ELSE) +// EB_scf_if(COND, RESULT_TYPES) +#define EB_scf_if(...) \ + for (auto &&eb_mlir_if_scope__ : \ + ::mlir::easybuild::makeScfIfLikeRange(__VA_ARGS__)) \ + if (eb_mlir_if_scope__ == 0) +#define EB_else else + } // namespace easybuild } // namespace mlir #endif diff --git a/unittests/Dialect/SCF/EasyBuildTest.cpp b/unittests/Dialect/SCF/EasyBuildTest.cpp index 475216b76..0f07ba13b 100644 --- a/unittests/Dialect/SCF/EasyBuildTest.cpp +++ b/unittests/Dialect/SCF/EasyBuildTest.cpp @@ -20,6 +20,7 @@ class SCFTest : public ::testing::Test { protected: SCFTest() { context.getOrLoadDialect(); + context.getOrLoadDialect(); context.getOrLoadDialect(); } @@ -44,9 +45,31 @@ TEST_F(SCFTest, EasyBuild) { /*ind_var*/ ValueRange{init_val}); EB_for(auto &&[idx, redu] : forRangeIn(b, loop)) { auto idx2 = idx + b.toIndex(1); - b.F(ValueRange{idx2 + redu}); + EB_scf_if(idx2 == b.toIndex(10), false) { + // if without else + b.toIndex(1123); + } + EB_scf_if(idx2 == b.toIndex(12)) { + // if-else, no return value + b.toIndex(1123); + } + EB_else { + // else + b.toIndex(11234); + } + EB_scf_if(idx2 == b.toIndex(14), {builder.getIndexType()}) { + // if-else with return value + b.yield(idx); + } + EB_else { + // else with return value + b.yield(idx2); + } + auto ifResult = b.wrap(b.getLastOperaion()->getResult(0)); + b.yield(ifResult + redu); } - builder.create(loc, loop.getResult(0)); + b.yield(loop.getResult(0)); + std::string out; llvm::raw_string_ostream os{out}; os << func; @@ -61,8 +84,27 @@ TEST_F(SCFTest, EasyBuild) { %1 = scf.for %arg1 = %c0 to %c10_0 step %c1 iter_args(%arg2 = %0) -> (index) { %c1_1 = arith.constant 1 : index %2 = arith.addi %arg1, %c1_1 : index - %3 = arith.addi %2, %arg2 : index - scf.yield %3 : index + %c10_2 = arith.constant 10 : index + %3 = arith.cmpi eq, %2, %c10_2 : index + scf.if %3 { + %c1123 = arith.constant 1123 : index + } + %c12 = arith.constant 12 : index + %4 = arith.cmpi eq, %2, %c12 : index + scf.if %4 { + %c1123 = arith.constant 1123 : index + } else { + %c11234 = arith.constant 11234 : index + } + %c14 = arith.constant 14 : index + %5 = arith.cmpi eq, %2, %c14 : index + %6 = scf.if %5 -> (index) { + scf.yield %arg1 : index + } else { + scf.yield %2 : index + } + %7 = arith.addi %6, %arg2 : index + scf.yield %7 : index } return %1 : index })mlir"; From 537aea72d87b0a145caa0073380360b0323085e6 Mon Sep 17 00:00:00 2001 From: "Mei, Yijie" Date: Sat, 11 May 2024 15:27:19 +0800 Subject: [PATCH 3/4] update doc --- docs/EasyBuilder.md | 154 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 2 deletions(-) diff --git a/docs/EasyBuilder.md b/docs/EasyBuilder.md index f9f018977..aed2a7066 100644 --- a/docs/EasyBuilder.md +++ b/docs/EasyBuilder.md @@ -13,7 +13,12 @@ verbose and hard to read. The easy-builder utilities are introduced to make it easier to develop C++ code building complex IR. Easy-builder is not designed to replace the `OpBuilder`. Instead, it is built upon that, and serves as supplementary IR builder for complex cases, like heavily using `arith` and -`scf` operations. +`scf` operations. Moreover, easy builder is designed to extendable for general +dialects, not just for `arith` and `scf`. + +## Examples using easy-build + +This sections shows examples of using easy-builder to build complex IR in C++. An example code to build IR `(x+y-10)/(x-y+1)`, where `x` and `y` are unsigned 16-bit integers: @@ -51,6 +56,49 @@ auto x_m_y_p1 = builder.create(loc, x_m_y, v1); Value result = builder.create(loc, x_p_y_m10, x_m_y_p1); ``` +Another example to generate SCF operations of nested control flow of `scf.if` +and `scf.for`: + +```c++ +auto init_val = b.wrap(func.getArgument(0)); +auto loop = builder.create(loc, /*lo*/ b.toIndex(0), + /*upper*/ b.toIndex(10), + /*step*/ b.toIndex(1), + /*ind_var*/ ValueRange{init_val}); +EB_for(auto &&[idx, redu] : forRangeIn(b, loop)) { + EB_scf_if(idx == b.toIndex(0), {builder.getIndexType()}) { + b.yield(idx); + } EB_else { + b.yield(idx + redu); + } + auto ifResult = b.wrap(b.getLastOperaion()->getResult(0)); + b.yield(ifResult); +} +b.yield(loop.getResult(0)); +``` + +The code will generate the function body of below MLIR: + +```mlir +func.func @funcname(%init_val: index) -> index { + %c1 = arith.constant 1 : index + %c10 = arith.constant 10 : index + %c0 = arith.constant 0 : index + %0 = scf.for %idx = %c0 to %c10 step %c1 iter_args(%redu = %init_val) -> (index) { + %c0_0 = arith.constant 0 : index + %1 = arith.cmpi eq, %idx, %c0_0 : index + %ifResult = scf.if %1 -> (index) { + scf.yield %idx : index + } else { + %3 = arith.addi %idx, %redu : index + scf.yield %3 : index + } + scf.yield %ifResult : index + } + return %0 : index +} +``` + [TOC] ## Overall Design @@ -292,7 +340,109 @@ arith operations that is sensitive to the signess, like `divsi` or `divui`. ## Easy-build for general structured-control-flow -TBD +Easy-build supports generating `for-like` and `if-like` operations from any +dialects. + +### Generating for-loops + +Easy-build provides macro `EB_for` and function `forRangeIn` to generate loop +body IR inside of a loop op, which is `for-like` operaions. `for-like` is +defined as `LoopLikeOpInterface` operations, which has single block as body, and +whose loop iterator and loop-carried variables are the arguments of the single +block. `scf.for`, `scf.parallel` and `scf.forall` are both `for-like`. + +To use easy-build on a for-loop, the developer should first create a loop using +`OpBuilder::create`, and use `forRangeIn` with `EB_for` to build the IR of +the loop-body, as if writing C++ range-based for-loops: + +```C++ +auto loop = builder.create(...); +EB_for(auto &&[idx, redu] : forRangeIn(b, loop)) { + // code to generate loop body +} +``` + +The function `forRangeIn` takes variadic template type parameters, as the +expected wrapped `EBValue` types for the loop body block arguments. The above +code expects the `loop` to have one loop iterator and one loop-carried variable, +and both to be wrapped in `EBUnsigned`. + +The macro `EB_for` expands to normal C++ `for` keyword, to mark that the +for-loop is a easy-build loop for building IR. Developers can use `auto &&[...]` +to unpack the loop body block arguments extracted in `forRangeIn`. The above +code `idx, redu` binds to the 2 arguments of the `loop`'s body. They will have +C++ types of `EBUnsigned`. Developers can further use the variables in IR +generation. + +The code which generates IR with easy-build in a `EB_for` will insert IR to the +corresponding loop body block. The feature is implemented in `forRangeIn`, which +creates a RAII object to set the insertion point of the `OpBuilder` to the loop +body at constructor and resets the insertion point after the loop operation at +its destructor. The C++ code ourside of a `EB_for` will correctly insert IR +after the loop. + +### Generating if-else + +Similar to for-loops, easy-build supports to build `if-like` operations in +similar way of writing `if-else` in C++. `if-like` is defined as operations +having two scopes and one block for each scope. The first scope is the `then` +block and the second is optional and is the `else` block. `scf.if` is `if-like`. + +Easy-build provides macros `EB_if`, `EB_else` and a function `makeIfRange` for +build `if-else`. General code to build `if-else` is: + +```C++ +auto ifelse = builder.create(...); +EB_if(makeIfRange(b, ifelse)) { + // generate then-block +} EB_else { + // generate else-block +} +``` + +Developers can put the C++ code inside the `then-block` or `else-block` above to +insert IR to the `then` or `else` blocks inside of the operation `ifelse` above. +The else block `EB_else { ... }` is optional if `else` block is not defined in +the `ifelse` operation. Outside and after `EB_if`, the insertion point of the +underlying `OpBuilder` will be set after the `ifelse` operation. + +Easy-build further provides an easier-to-use macro for `scf.if`. Developers can +build `scf.if` and start a `EB_if` scope in a single macro `EB_scf_if`, to make +the code look closer to C++ `if-else`. + +To generate `scf.if` with `else` and without result values: + +```C++ +EB_scf_if(pred) { + ... +} EB_else { + ... +} +``` + +`pred` above should be a value of `EBValue` or its subclasses. It can even be a +C++ expression of `EBValue`, like `idx == b.toIndex(10)`. + +To generate `scf.if` without `else` and without result values: + +```C++ +EB_scf_if(pred, false) { + ... +} +``` + +To generate `scf.if` with result values: + +```C++ +EB_scf_if(pred, {yielded_type,...}) { + ... + b.yield(...); +} EB_else { + ... + b.yield(...); +} +auto ifResult = b.getLastOperaion()->getResult(0); +``` ## Extending easy-build for dialects From 9061681b41c4b9ed36bb35c8d87078865f63d822 Mon Sep 17 00:00:00 2001 From: "Mei, Yijie" Date: Fri, 14 Jun 2024 11:04:44 +0800 Subject: [PATCH 4/4] fix --- include/gc/Dialect/Arith/Utils/EasyBuild.h | 14 +++++--------- include/gc/IR/EasyBuildSCF.h | 7 ++++--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/include/gc/Dialect/Arith/Utils/EasyBuild.h b/include/gc/Dialect/Arith/Utils/EasyBuild.h index 2a45ffcee..38329637b 100644 --- a/include/gc/Dialect/Arith/Utils/EasyBuild.h +++ b/include/gc/Dialect/Arith/Utils/EasyBuild.h @@ -28,12 +28,8 @@ namespace impl { template struct ToFloatType {}; -template <> struct ToFloatType<4> { - using type = Float32Type; -}; -template <> struct ToFloatType<8> { - using type = Float64Type; -}; +template <> struct ToFloatType<4> { using type = Float32Type; }; +template <> struct ToFloatType<8> { using type = Float64Type; }; inline Type getElementType(Value v) { auto type = v.getType(); @@ -275,13 +271,13 @@ inline EBFloatPoint operator-(const EBFloatPoint &a) { } #define DEF_EASYBUILD_CMP_OPERATOR(OP, OPCLASS, TYPE, PRED) \ - EBUnsigned operator OP(const TYPE &a, const TYPE &b) { \ + inline EBUnsigned operator OP(const TYPE &a, const TYPE &b) { \ return OperatorHandlers::handleCmp(a, b, PRED); \ } \ - template EBUnsigned operator OP(const TYPE &a, T b) { \ + template inline EBUnsigned operator OP(const TYPE &a, T b) { \ return OperatorHandlers::handleCmpConst(a, b, PRED); \ } \ - template EBUnsigned operator OP(T a, const TYPE &b) { \ + template inline EBUnsigned operator OP(T a, const TYPE &b) { \ return OperatorHandlers::handleCmpConst(a, b, PRED); \ } diff --git a/include/gc/IR/EasyBuildSCF.h b/include/gc/IR/EasyBuildSCF.h index 4f29728f0..27d98b5d7 100644 --- a/include/gc/IR/EasyBuildSCF.h +++ b/include/gc/IR/EasyBuildSCF.h @@ -149,19 +149,20 @@ inline int IfIterator::operator*() const { } // namespace impl -impl::IfSimulator makeIfRange(const EasyBuilder &s, Operation *op) { +inline impl::IfSimulator makeIfRange(const EasyBuilder &s, Operation *op) { return impl::IfSimulator{s.builder, op}; } template -impl::IfSimulator makeScfIfLikeRange(EBValue cond, TypeRange resultTypes) { +inline impl::IfSimulator makeScfIfLikeRange(EBValue cond, + TypeRange resultTypes) { auto &s = cond.builder; auto op = s->builder.create(s->loc, resultTypes, cond, true); return impl::IfSimulator{s, op}; } template -impl::IfSimulator makeScfIfLikeRange(EBValue cond, bool hasElse = true) { +inline impl::IfSimulator makeScfIfLikeRange(EBValue cond, bool hasElse = true) { auto &s = cond.builder; auto op = s->builder.create(s->loc, TypeRange{}, cond, hasElse); return impl::IfSimulator{s, op};