Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
4bd8221
mlir: fixed reg for iter bug
gf712 May 8, 2026
58f419e
mlir: migrate to LLVM/MLIR 23 API
gf712 May 9, 2026
3d2340a
mlir: bounds-check sorted-block lookup in JumpIf emission
gf712 May 10, 2026
0ba0a72
mlir: emit emitpybytecode.LOAD_CONST in BuildList lowering
gf712 May 10, 2026
319b3b3
mlir: drop unused SSABuilder
gf712 May 10, 2026
d25bb99
mlir: remote commented-out debug prints
gf712 May 10, 2026
3090759
mlir: mark ConstantOp Pure in both Python and EmitPythonBytecode dial…
gf712 May 10, 2026
86dff14
mlir: add canonicalize + CSE after the bytecode lowering pass
gf712 May 10, 2026
b5826df
mlir: implement getSuccessorInputs for RegionBranchOpInterface ops
gf712 May 10, 2026
c2365b4
mlir: add value-attribute verifier to ConstantOp
gf712 May 10, 2026
f42bae9
mlir: mark Store* and Delete* ops as MemWrite
gf712 May 10, 2026
927f0cc
mlir: re-enable region simplification in the lowering pass
gf712 May 10, 2026
d17bd36
mlir: document why pre-lowering canonicalize is skipped
gf712 May 10, 2026
a5d4e1c
mlir: mark LoadEllipsis / LoadAssertionError / LoadBuildClass Pure
gf712 May 10, 2026
4a81587
mlir: dedup lowering pass logic
gf712 May 10, 2026
45e1c1f
mlir: extract make_function helpers as free functions
gf712 May 11, 2026
048763e
mlir: model may-raise side effect on Load* ops
gf712 May 12, 2026
bd79f8d
mlir: enable pre-lowering canonicalize + CSE on the Python dialect
gf712 May 12, 2026
3eb6b4b
mlir: forward py.store_fast values to py.load_fast within a block
gf712 May 12, 2026
88d7c10
mlir: implement ControlFlowYield::getSuccessorRegions
gf712 May 12, 2026
0db32e6
mlir: collapse ControlFlowYield Default arm to a single unreachable
gf712 May 13, 2026
56c19de
mlir: replace RaiseOp get_handler default arm TODO() with assert
gf712 May 13, 2026
98c86f5
mlir: rename Compare to CompareOp
gf712 May 13, 2026
d1a8d15
mlir: fix colon spacing in PythonOps.td record definitions
gf712 May 13, 2026
cf56e8c
mlir: add NamedOp trait verifying non-empty name attribute
gf712 May 13, 2026
2fcb97f
mlir: add shape verifiers for Build{Dict,List,Tuple,Set} ops
gf712 May 13, 2026
ffaa19d
mlir: declare MemoryEffects on mutating ops
gf712 May 13, 2026
b01e625
mlir: collapse arithmetic binary ops into a single py.binary
gf712 May 13, 2026
e1cfed1
mlir: fold LogicalAnd/Or/XorOp into py.binary
gf712 May 13, 2026
50b471a
mlir: rename ControlFlowYield to BranchYieldOp
gf712 May 13, 2026
74bd6b3
mlir: rename TryHandlerScope to TryHandlerOp
gf712 May 13, 2026
d6341a7
mlir: factor BuildList/BuildTuple expansion loop into a helper
gf712 May 13, 2026
998eaf5
mlir: document why createRemoveDeadValuesPass cannot be enabled
gf712 May 13, 2026
1fce776
mlir: document the large-literal register-pressure workarounds
gf712 May 13, 2026
7eac126
mlir: introduce dedicated passes for ForLoop/While/Try/With lowerings
gf712 May 13, 2026
6718da7
mlir: route control-flow lowering through dedicated passes
gf712 May 13, 2026
2b0bcde
mlir: document why register allocation cannot trivially become a pass
gf712 May 13, 2026
4208561
mlir: add lit/FileCheck harness + python-mlir-opt tool
gf712 May 13, 2026
98a742f
mlir: remove dead py.for_iter op from the Python dialect
gf712 May 13, 2026
e7c0e2d
mlir: drop unused Python_PyEllipsisType, use EllipsisAttr directly
gf712 May 13, 2026
7be5ff8
mlir: drop unused result from Store{Name,Fast,Global,Deref} ops
gf712 May 13, 2026
529924f
mlir: unify Python_{Binary,Inplace}OpKindAttr into Python_ArithOpKind…
gf712 May 13, 2026
f411272
mlir: mark PythonOps as MemWrite
gf712 May 13, 2026
b89e2f3
mlir: add py.call verifier for keywords/kwargs shape invariants
gf712 May 13, 2026
faa5353
mlir: add py.class verifier for keywords/kwargs shape
gf712 May 13, 2026
793783e
mlir: add py.unpack verifier rejecting zero results
gf712 May 13, 2026
386cf0f
mlir: make py.build_slice.step Optional<>
gf712 May 13, 2026
b599de9
mlir: switch to StrArrayAttr
gf712 May 13, 2026
cada228
mlir: Add dialect description to EmitPythonBytecode
gf712 May 13, 2026
d6a9482
mlir: split off patterns from PythonToPythonBytecode.cpp
gf712 May 13, 2026
4e9e3d8
mlir: drop now-unused conversion-pass helper duplicates
gf712 May 14, 2026
75cdbf8
mlir: add FileCheck conversion test for py.for_loop lowering
gf712 May 14, 2026
e7e1594
mlir: pin multi-item with-statement gap with an assert
gf712 May 14, 2026
192eb81
mlir: move large-literal register-pressure rewrite to canonicalize
gf712 May 14, 2026
2905cb7
mlir: delete dead ConversionTarget setup, document applyPatternsGreed…
gf712 May 14, 2026
6d7935a
mlir: opt-in IR-printing between passes via MLIR_PRINT_IR_AFTER_ALL
gf712 May 14, 2026
621054a
mlir: add MaterialiseReturnNonePass to back up createRemoveDeadValues…
gf712 May 14, 2026
96805b2
mlir: enable RemoveDeadValues
gf712 May 14, 2026
f2d126b
mlir: delegate block-level liveness to mlir::Liveness
gf712 May 14, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/premerge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ jobs:
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 20
sudo apt install libmlir-20-dev mlir-20-tools
sudo ./llvm.sh 23
sudo apt install libmlir-23-dev mlir-23-tools

- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
Expand Down
21 changes: 21 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
"CMAKE_BUILD_TYPE": "Release",
"CPM_SOURCE_CACHE": ".cache/CPM"
}
},
{
"name": "release-with-debug-info",
"displayName": "Release with Debug Info",
"generator": "Ninja",
"binaryDir": "build/release-with-debug-info",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"CPM_SOURCE_CACHE": ".cache/CPM"
}
}
],
"buildPresets": [
Expand All @@ -34,6 +44,12 @@
"displayName": "Release Build",
"configurePreset": "release",
"configuration": "Release"
},
{
"name": "release-with-debug-info",
"displayName": "Release with Debug Info Build",
"configurePreset": "release-with-debug-info",
"configuration": "RelWithDebInfo"
}
],
"testPresets": [
Expand All @@ -46,6 +62,11 @@
"name": "release",
"displayName": "Test all in Release mode",
"configurePreset": "release"
},
{
"name": "release-with-debug-info",
"displayName": "Test all in Release with Debug Info mode",
"configurePreset": "release-with-debug-info"
}
]

Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ target_link_libraries(python-cpp
)

# LLVM backend
find_package(LLVM CONFIG 20.1)
find_package(LLVM CONFIG 23)
if(ENABLE_LLVM_BACKEND AND NOT LLVM_FOUND)
message(FATAL_ERROR "Could not find LLVM in the local environment")
elseif(ENABLE_LLVM_BACKEND AND LLVM_FOUND)
Expand Down
4 changes: 3 additions & 1 deletion src/executable/bytecode/Bytecode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ py::PyResult<py::Value> Bytecode::eval_loop(VirtualMachine &vm, Interpreter &int
ASSERT((*vm.instruction_pointer()).get());
const auto &current_ip = vm.instruction_pointer();
const auto &instruction = *current_ip;
spdlog::debug("{} {}", (void *)instruction.get(), instruction->to_string());
// spdlog::debug("{} {}", (void *)instruction.get(), instruction->to_string());
// std::cout << std::format("{} {}", (void *)instruction.get(), instruction->to_string())
// << std::endl;
auto result = instruction->execute(vm, vm.interpreter());
// we left the current stack frame in the previous instruction
if (vm.stack().size() != stack_depth) {
Expand Down
11 changes: 11 additions & 0 deletions src/executable/bytecode/instructions/ListToTuple.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include "ListToTuple.hpp"
#include "runtime/PyList.hpp"
#include "runtime/PyString.hpp"
#include "runtime/PyTuple.hpp"
#include "vm/VM.hpp"

#include <iostream>

using namespace py;

PyResult<Value> ListToTuple::execute(VirtualMachine &vm, Interpreter &) const
Expand All @@ -12,6 +15,14 @@ PyResult<Value> ListToTuple::execute(VirtualMachine &vm, Interpreter &) const
ASSERT(std::holds_alternative<PyObject *>(list));

auto *pylist = std::get<PyObject *>(list);
if (!as<PyList>(pylist)) {
std::cout << to_string() << std::endl;
if (!pylist) {
std::cout << "(null)" << std::endl;
} else {
std::cout << pylist->str().unwrap()->to_string() << std::endl;
}
}
ASSERT(as<PyList>(pylist));

auto result = PyTuple::create(as<PyList>(pylist)->elements());
Expand Down
2 changes: 1 addition & 1 deletion src/executable/bytecode/instructions/YieldFrom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ PyResult<Value> YieldFrom::execute(VirtualMachine &vm, Interpreter &interpreter)
vm.reg(m_dst) = result.unwrap();
vm.reg(0) = result.unwrap();
vm.set_instruction_pointer(vm.instruction_pointer() - 1);
vm.pop_frame(true);
vm.pop_frame(false);
}

return result;
Expand Down
2 changes: 1 addition & 1 deletion src/executable/bytecode/instructions/YieldValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ PyResult<Value> YieldValue::execute(VirtualMachine &vm, Interpreter &interpreter

vm.reg(0) = result;

vm.pop_frame(true);
vm.pop_frame(false);

return Ok(result);
}
Expand Down
1 change: 1 addition & 0 deletions src/executable/bytecode/serialization/deserialize.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "serialize.hpp"
#include "utilities.hpp"

#include <iostream>
#include <span>
#include <string>

Expand Down
4 changes: 3 additions & 1 deletion src/executable/mlir/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
find_package(LLVM 20.1 REQUIRED CONFIG)
find_package(LLVM 23 REQUIRED CONFIG)
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")

find_package(MLIR CONFIG REQUIRED
Expand All @@ -20,6 +20,8 @@ set(PYTHON_MLIR_BINARY_DIR ${PROJECT_BINARY_DIR}/src/executable/mlir)
add_subdirectory(Conversion)
add_subdirectory(Dialect)
add_subdirectory(Target)
add_subdirectory(tools/python-mlir-opt)
add_subdirectory(test)

add_library(python-mlir compile.cpp)
target_link_libraries(python-mlir PRIVATE PythonMLIRDialect TargetPythonBytecode PythonConversionPasses)
26 changes: 26 additions & 0 deletions src/executable/mlir/Conversion/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,30 @@ include "mlir/Pass/PassBase.td"
def ConvertPythonToPythonBytecode : Pass<"convert-python-to-pythonbytecode"> {
let summary = "Convert recognized Python ops to PythonCpp bytecode";
let constructor = "mlir::py::createPythonToPythonBytecodePass()";
}

def ConvertPyForLoop : Pass<"convert-py-forloop"> {
let summary = "Lower py.for_loop to emitpybytecode control flow";
let constructor = "mlir::py::createConvertForLoopPass()";
}

def ConvertPyWhile : Pass<"convert-py-while"> {
let summary = "Lower py.while to emitpybytecode control flow";
let constructor = "mlir::py::createConvertWhileLoopPass()";
}

def ConvertPyTry : Pass<"convert-py-try"> {
let summary = "Lower py.try (and its handler scopes) to emitpybytecode control flow";
let constructor = "mlir::py::createConvertTryPass()";
}

def ConvertPyWith : Pass<"convert-py-with"> {
let summary = "Lower py.with to emitpybytecode control flow";
let constructor = "mlir::py::createConvertWithPass()";
}

def MaterialiseReturnNone : Pass<"materialise-return-none", "::mlir::func::FuncOp"> {
let summary = "Insert emitpybytecode.LOAD_CONST(None) before zero-operand func.return ops";
let constructor = "mlir::py::createMaterialiseReturnNonePass()";
let dependentDialects = ["::mlir::emitpybytecode::EmitPythonBytecodeDialect"];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#include "Conversion/PythonToPythonBytecode/LoweringHelpers.hpp"
#include "Conversion/PythonToPythonBytecode/PatternPopulators.hpp"

#include "Dialect/EmitPythonBytecode/IR/EmitPythonBytecode.hpp"
#include "Dialect/Python/IR/PythonOps.hpp"

#include "executable/bytecode/instructions/BinaryOperation.hpp"
#include "executable/bytecode/instructions/Unary.hpp"
#include "utilities.hpp"

#include "mlir/IR/PatternMatch.h"

namespace mlir::py {
namespace {

// Translate py.{binary,inplace_op}'s ArithOpKind enum to the
// bytecode-level BinaryOperation::Operation enum. The two are
// deliberately decoupled: the dialect enum is part of the IR
// contract; the bytecode enum is the wire format consumed by the
// VM, so the mapping between the two must stay explicit.
BinaryOperation::Operation py_kind_to_binary_op(mlir::py::ArithOpKind kind)
{
switch (kind) {
case mlir::py::ArithOpKind::add:
return BinaryOperation::Operation::PLUS;
case mlir::py::ArithOpKind::sub:
return BinaryOperation::Operation::MINUS;
case mlir::py::ArithOpKind::mod:
return BinaryOperation::Operation::MODULO;
case mlir::py::ArithOpKind::mul:
return BinaryOperation::Operation::MULTIPLY;
case mlir::py::ArithOpKind::exp:
return BinaryOperation::Operation::EXP;
case mlir::py::ArithOpKind::div:
return BinaryOperation::Operation::SLASH;
case mlir::py::ArithOpKind::fldiv:
return BinaryOperation::Operation::FLOORDIV;
case mlir::py::ArithOpKind::mmul:
return BinaryOperation::Operation::MATMUL;
case mlir::py::ArithOpKind::lshift:
return BinaryOperation::Operation::LEFTSHIFT;
case mlir::py::ArithOpKind::rshift:
return BinaryOperation::Operation::RIGHTSHIFT;
case mlir::py::ArithOpKind::and_:
return BinaryOperation::Operation::AND;
case mlir::py::ArithOpKind::or_:
return BinaryOperation::Operation::OR;
case mlir::py::ArithOpKind::xor_:
return BinaryOperation::Operation::XOR;
}
ASSERT_NOT_REACHED();
}

struct InplaceOpLowering : public mlir::OpRewritePattern<InplaceOp>
{
using OpRewritePattern<InplaceOp>::OpRewritePattern;

mlir::LogicalResult matchAndRewrite(InplaceOp op,
mlir::PatternRewriter &rewriter) const final
{
auto op_type = mlir::IntegerAttr::get(rewriter.getIntegerType(8, false),
static_cast<uint8_t>(py_kind_to_binary_op(op.getKind())));

rewriter.replaceOpWithNewOp<mlir::emitpybytecode::InplaceOp>(
op, op.getResult().getType(), op.getDst(), op.getSrc(), op_type);

return success();
}
};

struct BinaryOpLowering : public mlir::OpRewritePattern<mlir::py::BinaryOp>
{
using OpRewritePattern<mlir::py::BinaryOp>::OpRewritePattern;

mlir::LogicalResult matchAndRewrite(mlir::py::BinaryOp op,
mlir::PatternRewriter &rewriter) const final
{
auto op_type = mlir::IntegerAttr::get(rewriter.getIntegerType(8, false),
static_cast<uint8_t>(py_kind_to_binary_op(op.getKind())));
rewriter.replaceOpWithNewOp<mlir::emitpybytecode::BinaryOp>(
op, op.getOutput().getType(), op.getLhs(), op.getRhs(), op_type);
return success();
}
};

// Trivial 1:1 lowering of a py.unary_* op to emitpybytecode.UNARY_OP
// with the corresponding Unary::Operation enum baked in.
template<typename From, Unary::Operation Kind>
struct UnaryOpLowering : public mlir::OpRewritePattern<From>
{
using mlir::OpRewritePattern<From>::OpRewritePattern;

mlir::LogicalResult matchAndRewrite(From op, mlir::PatternRewriter &rewriter) const final
{
rewriter.template replaceOpWithNewOp<mlir::emitpybytecode::UnaryOp>(
op, op.getOutput().getType(), op.getInput(), static_cast<uint8_t>(Kind));
return mlir::success();
}
};

using PositiveOpLowering = UnaryOpLowering<mlir::py::PositiveOp, Unary::Operation::POSITIVE>;
using NegativeOpLowering = UnaryOpLowering<mlir::py::NegativeOp, Unary::Operation::NEGATIVE>;
using InvertOpLowering = UnaryOpLowering<mlir::py::InvertOp, Unary::Operation::INVERT>;
using NotOpLowering = UnaryOpLowering<mlir::py::NotOp, Unary::Operation::NOT>;

struct CompareOpLowering : public mlir::OpRewritePattern<mlir::py::CompareOp>
{
using OpRewritePattern<mlir::py::CompareOp>::OpRewritePattern;

mlir::LogicalResult matchAndRewrite(mlir::py::CompareOp op,
mlir::PatternRewriter &rewriter) const final
{
auto lhs = op.getLhs();
auto rhs = op.getRhs();
auto op_type = mlir::IntegerAttr::get(
rewriter.getIntegerType(8, false), static_cast<uint8_t>(op.getPredicate()));

rewriter.replaceOpWithNewOp<mlir::emitpybytecode::Compare>(
op, op.getOutput().getType(), lhs, rhs, op_type);

return success();
}
};

struct CastToBoolOpLowering : public mlir::OpRewritePattern<mlir::py::CastToBoolOp>
{
using OpRewritePattern<mlir::py::CastToBoolOp>::OpRewritePattern;

mlir::LogicalResult matchAndRewrite(mlir::py::CastToBoolOp op,
mlir::PatternRewriter &rewriter) const final
{
rewriter.replaceOpWithNewOp<mlir::emitpybytecode::CastToBool>(
op, op.getValue().getType(), op.getValue());
return success();
}
};

}// namespace

void populateArithPatterns(mlir::RewritePatternSet &patterns)
{
auto *ctx = patterns.getContext();
patterns.add<BinaryOpLowering, InplaceOpLowering, CompareOpLowering, CastToBoolOpLowering>(ctx);
patterns.add<PositiveOpLowering, NegativeOpLowering, InvertOpLowering, NotOpLowering>(ctx);
}

}// namespace mlir::py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include "Conversion/PythonToPythonBytecode/LoweringHelpers.hpp"
#include "Conversion/PythonToPythonBytecode/PatternPopulators.hpp"

#include "Dialect/EmitPythonBytecode/IR/EmitPythonBytecode.hpp"
#include "Dialect/Python/IR/PythonOps.hpp"

#include "mlir/IR/PatternMatch.h"

namespace mlir::py {
namespace {

using LoadAttributeOpLowering = detail::DirectReplaceLowering<mlir::py::LoadAttributeOp,
mlir::emitpybytecode::LoadAttribute>;

using DeleteAttributeOpLowering = detail::DirectReplaceLowering<mlir::py::DeleteAttributeOp,
mlir::emitpybytecode::DeleteAttribute>;

using LoadMethodOpLowering = detail::DirectReplaceRegisterName<mlir::py::LoadMethodOp,
mlir::emitpybytecode::LoadMethod,
&mlir::py::LoadMethodOp::getMethodName>;

using BinarySubscriptOpLowering = detail::DirectReplaceLowering<mlir::py::BinarySubscriptOp,
mlir::emitpybytecode::BinarySubscript>;

using StoreSubscriptOpLowering = detail::DirectReplaceLowering<mlir::py::StoreSubscriptOp,
mlir::emitpybytecode::StoreSubscript>;

using DeleteSubscriptOpLowering = detail::DirectReplaceLowering<mlir::py::DeleteSubscriptOp,
mlir::emitpybytecode::DeleteSubscript>;

using StoreAttributeOpLowering = detail::DirectReplaceRegisterName<mlir::py::StoreAttributeOp,
mlir::emitpybytecode::StoreAttribute,
&mlir::py::StoreAttributeOp::getAttr>;

}// namespace

void populateAttributeSubscriptPatterns(mlir::RewritePatternSet &patterns)
{
auto *ctx = patterns.getContext();
patterns.add<LoadAttributeOpLowering,
DeleteAttributeOpLowering,
LoadMethodOpLowering,
BinarySubscriptOpLowering,
StoreSubscriptOpLowering,
DeleteSubscriptOpLowering,
StoreAttributeOpLowering>(ctx);
}

}// namespace mlir::py
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
add_mlir_conversion_library(PythonToPythonBytecode
PythonToPythonBytecode.cpp
ArithPatterns.cpp
AttributeSubscriptPatterns.cpp
CollectionPatterns.cpp
ControlFlowPatterns.cpp
FunctionPatterns.cpp
ImportPatterns.cpp
LoadStorePatterns.cpp

ADDITIONAL_HEADER_DIRS
${PROJECT_SOURCE_DIR}/src/executable/mlir/Conversion/PythonToPythonBytecode
Expand Down
Loading
Loading