Skip to content

Commit

Permalink
Add conversion for unary operations (#29)
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Korobeynikov <[email protected]>
  • Loading branch information
asl authored Feb 10, 2025
1 parent e60a5a8 commit c2259bc
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 19 deletions.
4 changes: 4 additions & 0 deletions include/p4mlir/Dialect/P4HIR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ mlir_tablegen(P4HIR_Ops.h.inc -gen-op-decls)
mlir_tablegen(P4HIR_Ops.cpp.inc -gen-op-defs)
mlir_tablegen(P4HIR_Types.h.inc -gen-typedef-decls -typedefs-dialect=p4hir)
mlir_tablegen(P4HIR_Types.cpp.inc -gen-typedef-defs -typedefs-dialect=p4hir)

# Generate extra headers for custom enum and attrs.
mlir_tablegen(P4HIR_OpsEnums.h.inc -gen-enum-decls)
mlir_tablegen(P4HIR_OpsEnums.cpp.inc -gen-enum-defs)
mlir_tablegen(P4HIR_Attrs.h.inc -gen-attrdef-decls -attrdefs-dialect=p4hir)
mlir_tablegen(P4HIR_Attrs.cpp.inc -gen-attrdef-defs -attrdefs-dialect=p4hir)

Expand Down
4 changes: 2 additions & 2 deletions include/p4mlir/Dialect/P4HIR/P4HIR_Ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
#include "mlir/Interfaces/InferTypeOpInterface.h"
#include "mlir/Interfaces/MemorySlotInterfaces.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_OpsEnums.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_Types.h"

#define GET_OP_CLASSES
#include "p4mlir/Dialect/P4HIR/P4HIR_Ops.h.inc"

#endif // P4MLIR_DIALECT_P4HIR_P4HIR_OPS_H
#endif // P4MLIR_DIALECT_P4HIR_P4HIR_OPS_H
39 changes: 39 additions & 0 deletions include/p4mlir/Dialect/P4HIR/P4HIR_Ops.td
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,43 @@ def CastOp : P4HIR_Op<"cast",
// FIXME: add verifier.
}

def UnaryOpKind_Neg : I32EnumAttrCase<"Neg", 1, "minus">; // unary minus (-)
def UnaryOpKind_UPlus : I32EnumAttrCase<"UPlus", 2, "plus">; // unary plus (+)
def UnaryOpKind_Cmpl : I32EnumAttrCase<"Cmpl", 3, "cmpl">; // complement (~)
def UnaryOpKind_LNot : I32EnumAttrCase<"LNot", 4, "not">; // logical not

def UnaryOpKind : I32EnumAttr<"UnaryOpKind",
"unary operation (arith and logic) kind",
[UnaryOpKind_Neg, UnaryOpKind_UPlus,
UnaryOpKind_Cmpl, UnaryOpKind_LNot]> {
let cppNamespace = "::P4::P4MLIR::P4HIR";
}

def UnaryOp : P4HIR_Op<"unary", [Pure, SameOperandsAndResultType]> {
let summary = "Unary operations";
let description = [{
`p4hir.unary` performs the unary operation according to
the specified opcode kind: [minus, plus, cmpl, not].

It requires one input operand and has one result, both types
should be the same.

```mlir
%7 = p4hir.unary(minus, %1) : i32
%8 = p4hir.unary(not, %2) : i32
```
}];

let results = (outs AnyP4Type:$result);
// FIXME: constraint types for unary operations
// FIXME: check type for lnot
let arguments = (ins Arg<UnaryOpKind, "unary op kind">:$kind, Arg<AnyP4Type>:$input);

let assemblyFormat = [{
`(` $kind `,` $input `)` `:` type($input) attr-dict
}];

let hasVerifier = 1;
}

#endif // P4MLIR_DIALECT_P4HIR_P4HIR_OPS_TD
11 changes: 11 additions & 0 deletions include/p4mlir/Dialect/P4HIR/P4HIR_OpsEnums.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef P4MLIR_DIALECT_P4HIR_P4HIR_OPSENUMS_H
#define P4MLIR_DIALECT_P4HIR_P4HIR_OPSENUMS_H

// We explicitly do not use push / pop for diagnostic in
// order to propagate pragma further on
#pragma GCC diagnostic ignored "-Wunused-parameter"

#include "mlir/IR/BuiltinAttributes.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_OpsEnums.h.inc"

#endif // P4MLIR_DIALECT_P4HIR_P4HIR_OPSENUMS_H
15 changes: 15 additions & 0 deletions lib/Dialect/P4HIR/P4HIR_Ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
#include "mlir/IR/DialectImplementation.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_Attrs.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_Dialect.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_OpsEnums.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_Types.h"

#define GET_OP_CLASSES
#include "p4mlir/Dialect/P4HIR/P4HIR_Dialect.cpp.inc"
#include "p4mlir/Dialect/P4HIR/P4HIR_Ops.cpp.inc"
#include "p4mlir/Dialect/P4HIR/P4HIR_OpsEnums.cpp.inc"

using namespace mlir;
using namespace P4::P4MLIR;
Expand Down Expand Up @@ -47,6 +49,19 @@ LogicalResult P4HIR::ConstOp::verify() {
return checkConstantTypes(getOperation(), getType(), getValue());
}

LogicalResult P4HIR::UnaryOp::verify() {
switch (getKind()) {
case P4HIR::UnaryOpKind::Neg:
case P4HIR::UnaryOpKind::UPlus:
case P4HIR::UnaryOpKind::Cmpl:
case P4HIR::UnaryOpKind::LNot:
// Nothing to verify.
return success();
}

llvm_unreachable("Unknown UnaryOp kind?");
}

//===----------------------------------------------------------------------===//
// AllocaOp
//===----------------------------------------------------------------------===//
Expand Down
13 changes: 13 additions & 0 deletions test/Dialect/P4HIR/unop.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: p4mlir-opt %s | FileCheck %s

// No need to check stuff. If it parses, it's fine.
// CHECK: module
module {
%0 = p4hir.const #p4hir.int<-128> : !p4hir.int<8>
%1 = p4hir.const #p4hir.bool<false> : !p4hir.bool

%2 = p4hir.unary(minus, %0) : !p4hir.int<8>
%3 = p4hir.unary(plus, %0) : !p4hir.int<8>
%4 = p4hir.unary(cmpl, %0) : !p4hir.int<8>
%5 = p4hir.unary(not, %1) : !p4hir.bool
}
36 changes: 36 additions & 0 deletions test/Translate/Ops/unop.p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: p4mlir-translate --typeinference-only %s | FileCheck %s

// NOTE: Assertions have been autogenerated by utils/generate-test-checks.py

// CHECK-LABEL: module
// CHECK-NEXT: %[[VAL_0:.*]] = p4hir.const #p4hir.bool<true> : !p4hir.bool
// CHECK: %[[VAL_1:.*]] = p4hir.alloca !p4hir.bool ["b0", init] : !p4hir.ref<!p4hir.bool>
// CHECK: p4hir.store %[[VAL_0]], %[[VAL_1]] : !p4hir.bool, !p4hir.ref<!p4hir.bool>
// CHECK: %[[VAL_2:.*]] = p4hir.const #p4hir.int<255> : !p4hir.int<32>
// CHECK: %[[VAL_3:.*]] = p4hir.alloca !p4hir.int<32> ["b2", init] : !p4hir.ref<!p4hir.int<32>>
// CHECK: p4hir.store %[[VAL_2]], %[[VAL_3]] : !p4hir.int<32>, !p4hir.ref<!p4hir.int<32>>
// CHECK: %[[VAL_4:.*]] = p4hir.load %[[VAL_3]] : !p4hir.ref<!p4hir.int<32>>, !p4hir.int<32>
// CHECK: %[[VAL_5:.*]] = p4hir.unary(minus, %[[VAL_4]]) : !p4hir.int<32>
// CHECK: %[[VAL_6:.*]] = p4hir.alloca !p4hir.int<32> ["b3", init] : !p4hir.ref<!p4hir.int<32>>
// CHECK: p4hir.store %[[VAL_5]], %[[VAL_6]] : !p4hir.int<32>, !p4hir.ref<!p4hir.int<32>>
// CHECK: %[[VAL_7:.*]] = p4hir.load %[[VAL_3]] : !p4hir.ref<!p4hir.int<32>>, !p4hir.int<32>
// CHECK: %[[VAL_8:.*]] = p4hir.unary(cmpl, %[[VAL_7]]) : !p4hir.int<32>
// CHECK: %[[VAL_9:.*]] = p4hir.alloca !p4hir.int<32> ["b4", init] : !p4hir.ref<!p4hir.int<32>>
// CHECK: p4hir.store %[[VAL_8]], %[[VAL_9]] : !p4hir.int<32>, !p4hir.ref<!p4hir.int<32>>
// CHECK: %[[VAL_10:.*]] = p4hir.load %[[VAL_1]] : !p4hir.ref<!p4hir.bool>, !p4hir.bool
// CHECK: %[[VAL_11:.*]] = p4hir.unary(not, %[[VAL_10]]) : !p4hir.bool
// CHECK: %[[VAL_12:.*]] = p4hir.alloca !p4hir.bool ["b5", init] : !p4hir.ref<!p4hir.bool>
// CHECK: p4hir.store %[[VAL_11]], %[[VAL_12]] : !p4hir.bool, !p4hir.ref<!p4hir.bool>
// CHECK: %[[VAL_13:.*]] = p4hir.load %[[VAL_3]] : !p4hir.ref<!p4hir.int<32>>, !p4hir.int<32>
// CHECK: %[[VAL_14:.*]] = p4hir.unary(plus, %[[VAL_13]]) : !p4hir.int<32>
// CHECK: %[[VAL_15:.*]] = p4hir.alloca !p4hir.int<32> ["b6", init] : !p4hir.ref<!p4hir.int<32>>
// CHECK: p4hir.store %[[VAL_14]], %[[VAL_15]] : !p4hir.int<32>, !p4hir.ref<!p4hir.int<32>>

action foo() {
bool b0 = true;
int<32> b2 = 32s0xFF;
int<32> b3 = -b2;
int<32> b4 = ~b2;
bool b5 = !b0;
int<32> b6 = +b2;
}
93 changes: 76 additions & 17 deletions tools/p4mlir-translate/translate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@
#include "ir/ir.h"
#include "ir/visitor.h"
#include "lib/big_int.h"
#include "lib/indent.h"
#include "lib/log.h"
#include "llvm/Support/raw_ostream.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_Attrs.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_Dialect.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_Ops.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_OpsEnums.h"
#include "p4mlir/Dialect/P4HIR/P4HIR_Types.h"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/Casting.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinAttributeInterfaces.h"
Expand Down Expand Up @@ -60,6 +63,15 @@ mlir::APInt toAPInt(const P4::big_int &value, unsigned bitWidth = 0) {
class P4HIRConverter;
class P4TypeConverter;

class ConversionTracer {
public:
ConversionTracer(const char *Kind, const P4::IR::Node *node) {
// TODO: Add TimeTrace here
LOG4(P4::IndentCtl::indent << Kind << dbp(node));
}
~ConversionTracer() { LOG4_UNINDENT; }
};

// A dedicated converter for conversion of the P4 types to their destination
// representation.
class P4TypeConverter : public P4::Inspector {
Expand Down Expand Up @@ -88,7 +100,7 @@ class P4TypeConverter : public P4::Inspector {
bool preorder(const P4::IR::Type_Boolean *type) override;
bool preorder(const P4::IR::Type_Unknown *type) override;
bool preorder(const P4::IR::Type_Typedef *type) override {
LOG4("TypeConverting " << dbp(type));
ConversionTracer trace("TypeConverting ", type);
visit(type->type);
return false;
}
Expand Down Expand Up @@ -206,6 +218,13 @@ class P4HIRConverter : public P4::Inspector, public P4::ResolutionContext {
}

mlir::Value setValue(const P4::IR::Node *node, mlir::Value value) {
if (LOGGING(4)) {
std::string s;
llvm::raw_string_ostream os(s);
value.print(os);
LOG4("Converted " << dbp(node) << " -> \"" << s << "\"");
}

auto [it, inserted] = p4Values.try_emplace(node, value);
BUG_CHECK(inserted, "duplicate conversion of %1%", node);
return it->second;
Expand All @@ -219,7 +238,7 @@ class P4HIRConverter : public P4::Inspector, public P4::ResolutionContext {
}

bool preorder(const P4::IR::Type *type) override {
LOG4("Converting " << dbp(type));
ConversionTracer trace("Converting ", type);
P4TypeConverter cvt(*this);
type->apply(cvt);
return false;
Expand Down Expand Up @@ -250,49 +269,57 @@ class P4HIRConverter : public P4::Inspector, public P4::ResolutionContext {
return false;
}

bool preorder(const P4::IR::Cast *cast) override { return true; }
#define HANDLE_IN_POSTORDER(NodeTy) \
bool preorder(const P4::IR::NodeTy *) override { return true; } \
void postorder(const P4::IR::NodeTy *) override;

bool preorder(const P4::IR::Declaration_Constant *decl) override;
HANDLE_IN_POSTORDER(Cast)
HANDLE_IN_POSTORDER(Neg)
HANDLE_IN_POSTORDER(LNot)
HANDLE_IN_POSTORDER(UPlus)
HANDLE_IN_POSTORDER(Cmpl)
HANDLE_IN_POSTORDER(Declaration_Variable)

#undef HANDLE_IN_POSTORDER

bool preorder(const P4::IR::Declaration_Variable *) override { return true; }
void postorder(const P4::IR::Declaration_Variable *decl) override;
void postorder(const P4::IR::Cast *cast) override;
mlir::Value emitUnOp(const P4::IR::Operation_Unary *unop, P4HIR::UnaryOpKind kind);
bool preorder(const P4::IR::Declaration_Constant *decl) override;
};

bool P4TypeConverter::preorder(const P4::IR::Type_Bits *type) {
if ((this->type = converter.findType(type))) return false;

LOG4("TypeConverting " << dbp(type));
ConversionTracer trace("TypeConverting ", type);
auto mlirType = P4HIR::BitsType::get(converter.context(), type->width_bits(), type->isSigned);
return setType(type, mlirType);
}

bool P4TypeConverter::preorder(const P4::IR::Type_InfInt *type) {
if ((this->type = converter.findType(type))) return false;

LOG4("TypeConverting " << dbp(type));
ConversionTracer trace("TypeConverting ", type);
auto mlirType = P4HIR::InfIntType::get(converter.context());
return setType(type, mlirType);
}

bool P4TypeConverter::preorder(const P4::IR::Type_Boolean *type) {
if ((this->type = converter.findType(type))) return false;

LOG4("TypeConverting " << dbp(type));
ConversionTracer trace("TypeConverting ", type);
auto mlirType = P4HIR::BoolType::get(converter.context());
return setType(type, mlirType);
}

bool P4TypeConverter::preorder(const P4::IR::Type_Unknown *type) {
if ((this->type = converter.findType(type))) return false;

LOG4("TypeConverting " << dbp(type));
ConversionTracer trace("TypeConverting ", type);
auto mlirType = P4HIR::UnknownType::get(converter.context());
return setType(type, mlirType);
}

bool P4TypeConverter::preorder(const P4::IR::Type_Name *name) {
LOG4("TypeConverting " << dbp(name));
ConversionTracer trace("TypeConverting ", name);
const auto *type = converter.resolveType(name);
CHECK_NULL(type);
visit(type);
Expand Down Expand Up @@ -352,17 +379,23 @@ mlir::TypedAttr P4HIRConverter::resolveConstantExpr(const P4::IR::Expression *ex
}

mlir::Value P4HIRConverter::materializeConstantExpr(const P4::IR::Expression *expr) {
LOG4("Materializing constant expression " << dbp(expr));
ConversionTracer trace("Materializing constant expression ", expr);

auto type = getOrCreateType(expr->type);
auto init = getOrCreateConstantExpr(expr);
auto loc = getLoc(builder, expr);

// Hack: type inference sometimes keeps `Type_Unknown` for some constants, in such case
// use type from the initializer
if (mlir::isa<P4HIR::UnknownType>(type)) type = init.getType();

auto val = builder.create<P4HIR::ConstOp>(loc, type, init);
return setValue(expr, val);
}

bool P4HIRConverter::preorder(const P4::IR::Declaration_Constant *decl) {
LOG4("Converting " << dbp(decl));
ConversionTracer trace("Converting ", decl);

auto type = getOrCreateType(decl->type);
auto init = getOrCreateConstantExpr(decl->initializer);
auto loc = getLoc(builder, decl);
Expand All @@ -374,7 +407,8 @@ bool P4HIRConverter::preorder(const P4::IR::Declaration_Constant *decl) {
}

void P4HIRConverter::postorder(const P4::IR::Declaration_Variable *decl) {
LOG4("Converting " << dbp(decl));
ConversionTracer trace("Converting ", decl);

const auto *init = decl->initializer;
mlir::Type objectType;
if (init) objectType = getOrCreateType(init);
Expand All @@ -396,13 +430,38 @@ void P4HIRConverter::postorder(const P4::IR::Declaration_Variable *decl) {
}

void P4HIRConverter::postorder(const P4::IR::Cast *cast) {
LOG4("Converting " << dbp(cast));
ConversionTracer trace("Converting ", cast);

auto src = getValue(cast->expr);
auto destType = getOrCreateType(cast->destType);

setValue(cast, builder.create<P4HIR::CastOp>(getLoc(builder, cast), destType, src));
}

mlir::Value P4HIRConverter::emitUnOp(const P4::IR::Operation_Unary *unop, P4HIR::UnaryOpKind kind) {
return builder.create<P4HIR::UnaryOp>(getLoc(builder, unop), kind, getValue(unop->expr));
}

void P4HIRConverter::postorder(const P4::IR::Neg *neg) {
ConversionTracer trace("Converting ", neg);
setValue(neg, emitUnOp(neg, P4HIR::UnaryOpKind::Neg));
}

void P4HIRConverter::postorder(const P4::IR::LNot *lnot) {
ConversionTracer trace("Converting ", lnot);
setValue(lnot, emitUnOp(lnot, P4HIR::UnaryOpKind::LNot));
}

void P4HIRConverter::postorder(const P4::IR::UPlus *plus) {
ConversionTracer trace("Converting ", plus);
setValue(plus, emitUnOp(plus, P4HIR::UnaryOpKind::UPlus));
}

void P4HIRConverter::postorder(const P4::IR::Cmpl *cmpl) {
ConversionTracer trace("Converting ", cmpl);
setValue(cmpl, emitUnOp(cmpl, P4HIR::UnaryOpKind::Cmpl));
}

} // namespace

namespace P4::P4MLIR {
Expand Down

0 comments on commit c2259bc

Please sign in to comment.