Skip to content

Commit

Permalink
[mlir][EmitC] Add member access ops (llvm#98460)
Browse files Browse the repository at this point in the history
This adds an `emitc.member` and `emitc.member_of_ptr` operation for the
corresponding member access operators. Furthermore, `emitc.assign` is
adjusted to be used with the member access operators.
  • Loading branch information
marbre authored Jul 13, 2024
1 parent c8f2ee7 commit a5cf99d
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 6 deletions.
42 changes: 42 additions & 0 deletions mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,48 @@ def EmitC_SubOp : EmitC_BinaryOp<"sub", [CExpression]> {
let hasVerifier = 1;
}

def EmitC_MemberOp : EmitC_Op<"member"> {
let summary = "Member operation";
let description = [{
With the `member` operation the member access operator `.` can be
applied.

Example:

```mlir
%0 = "emitc.member" (%arg0) {member = "a"}
: (!emitc.opaque<"mystruct">) -> i32
```
}];

let arguments = (ins
Arg<StrAttr, "the member to access">:$member,
EmitC_OpaqueType:$operand
);
let results = (outs EmitCType);
}

def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr"> {
let summary = "Member of pointer operation";
let description = [{
With the `member_of_ptr` operation the member access operator `->`
can be applied.

Example:

```mlir
%0 = "emitc.member_of_ptr" (%arg0) {member = "a"}
: (!emitc.ptr<!emitc.opaque<"mystruct">>) -> i32
```
}];

let arguments = (ins
Arg<StrAttr, "the member to access">:$member,
AnyTypeOf<[EmitC_OpaqueType,EmitC_PointerType]>:$operand
);
let results = (outs EmitCType);
}

def EmitC_ConditionalOp : EmitC_Op<"conditional",
[AllTypesMatch<["true_value", "false_value", "result"]>, CExpression]> {
let summary = "Conditional (ternary) operation";
Expand Down
7 changes: 4 additions & 3 deletions mlir/lib/Dialect/EmitC/IR/EmitC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,11 @@ LogicalResult emitc::AssignOp::verify() {
Value variable = getVar();
Operation *variableDef = variable.getDefiningOp();
if (!variableDef ||
!llvm::isa<emitc::GetGlobalOp, emitc::SubscriptOp, emitc::VariableOp>(
variableDef))
!llvm::isa<emitc::GetGlobalOp, emitc::MemberOp, emitc::MemberOfPtrOp,
emitc::SubscriptOp, emitc::VariableOp>(variableDef))
return emitOpError() << "requires first operand (" << variable
<< ") to be a get_global, subscript or variable";
<< ") to be a get_global, member, member of pointer, "
"subscript or variable";

Value value = getValue();
if (variable.getType() != value.getType())
Expand Down
34 changes: 32 additions & 2 deletions mlir/lib/Target/Cpp/TranslateToCpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ struct CppEmitter {
// Returns the textual representation of a subscript operation.
std::string getSubscriptName(emitc::SubscriptOp op);

// Returns the textual representation of a member (of object) operation.
std::string createMemberAccess(emitc::MemberOp op);

// Returns the textual representation of a member of pointer operation.
std::string createMemberAccess(emitc::MemberOfPtrOp op);

/// Return the existing or a new label of a Block.
StringRef getOrCreateName(Block &block);

Expand Down Expand Up @@ -278,8 +284,8 @@ struct CppEmitter {

/// Determine whether expression \p op should be emitted in a deferred way.
static bool hasDeferredEmission(Operation *op) {
return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp,
emitc::SubscriptOp>(op);
return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp, emitc::MemberOp,
emitc::MemberOfPtrOp, emitc::SubscriptOp>(op);
}

/// Determine whether expression \p expressionOp should be emitted inline, i.e.
Expand Down Expand Up @@ -1125,6 +1131,22 @@ std::string CppEmitter::getSubscriptName(emitc::SubscriptOp op) {
return out;
}

std::string CppEmitter::createMemberAccess(emitc::MemberOp op) {
std::string out;
llvm::raw_string_ostream ss(out);
ss << getOrCreateName(op.getOperand());
ss << "." << op.getMember();
return out;
}

std::string CppEmitter::createMemberAccess(emitc::MemberOfPtrOp op) {
std::string out;
llvm::raw_string_ostream ss(out);
ss << getOrCreateName(op.getOperand());
ss << "->" << op.getMember();
return out;
}

void CppEmitter::cacheDeferredOpResult(Value value, StringRef str) {
if (!valueMapper.count(value))
valueMapper.insert(value, str.str());
Expand Down Expand Up @@ -1501,6 +1523,14 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
cacheDeferredOpResult(op.getResult(), op.getValue());
return success();
})
.Case<emitc::MemberOp>([&](auto op) {
cacheDeferredOpResult(op.getResult(), createMemberAccess(op));
return success();
})
.Case<emitc::MemberOfPtrOp>([&](auto op) {
cacheDeferredOpResult(op.getResult(), createMemberAccess(op));
return success();
})
.Case<emitc::SubscriptOp>([&](auto op) {
cacheDeferredOpResult(op.getResult(), getSubscriptName(op));
return success();
Expand Down
18 changes: 17 additions & 1 deletion mlir/test/Dialect/EmitC/invalid_ops.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func.func @test_misplaced_yield() {
// -----

func.func @test_assign_to_non_variable(%arg1: f32, %arg2: f32) {
// expected-error @+1 {{'emitc.assign' op requires first operand (<block argument> of type 'f32' at index: 1) to be a get_global, subscript or variable}}
// expected-error @+1 {{'emitc.assign' op requires first operand (<block argument> of type 'f32' at index: 1) to be a get_global, member, member of pointer, subscript or variable}}
emitc.assign %arg1 : f32 to %arg2 : f32
return
}
Expand Down Expand Up @@ -450,3 +450,19 @@ func.func @use_global() {
%0 = emitc.get_global @myglobal : f32
return
}

// -----

func.func @member(%arg0: i32) {
// expected-error @+1 {{'emitc.member' op operand #0 must be EmitC opaque type, but got 'i32'}}
%0 = "emitc.member" (%arg0) {member = "a"} : (i32) -> i32
return
}

// -----

func.func @member_of_ptr(%arg0: i32) {
// expected-error @+1 {{'emitc.member_of_ptr' op operand #0 must be EmitC opaque type or EmitC pointer type, but got 'i32}}
%0 = "emitc.member_of_ptr" (%arg0) {member = "a"} : (i32) -> i32
return
}
7 changes: 7 additions & 0 deletions mlir/test/Dialect/EmitC/ops.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,10 @@ func.func @assign_global(%arg0 : i32) {
emitc.assign %arg0 : i32 to %0 : i32
return
}

func.func @member_access(%arg0: !emitc.opaque<"mystruct">, %arg1: !emitc.opaque<"mystruct_ptr">, %arg2: !emitc.ptr<!emitc.opaque<"mystruct">>) {
%0 = "emitc.member" (%arg0) {member = "a"} : (!emitc.opaque<"mystruct">) -> i32
%1 = "emitc.member_of_ptr" (%arg1) {member = "a"} : (!emitc.opaque<"mystruct_ptr">) -> i32
%2 = "emitc.member_of_ptr" (%arg2) {member = "a"} : (!emitc.ptr<!emitc.opaque<"mystruct">>) -> i32
return
}
34 changes: 34 additions & 0 deletions mlir/test/Target/Cpp/member.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s -check-prefix=CPP-DEFAULT

func.func @member(%arg0: !emitc.opaque<"mystruct">, %arg1: i32) {
%0 = "emitc.member" (%arg0) {member = "a"} : (!emitc.opaque<"mystruct">) -> i32
emitc.assign %arg1 : i32 to %0 : i32

%1 = "emitc.member" (%arg0) {member = "b"} : (!emitc.opaque<"mystruct">) -> i32
%2 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> i32
emitc.assign %1 : i32 to %2 : i32

return
}

// CPP-DEFAULT: void member(mystruct [[V0:[^ ]*]], int32_t [[V1:[^ ]*]]) {
// CPP-DEFAULT-NEXT: [[V0:[^ ]*]].a = [[V1:[^ ]*]];
// CPP-DEFAULT-NEXT: int32_t [[V2:[^ ]*]];
// CPP-DEFAULT-NEXT: [[V2:[^ ]*]] = [[V0:[^ ]*]].b;


func.func @member_of_pointer(%arg0: !emitc.ptr<!emitc.opaque<"mystruct">>, %arg1: i32) {
%0 = "emitc.member_of_ptr" (%arg0) {member = "a"} : (!emitc.ptr<!emitc.opaque<"mystruct">>) -> i32
emitc.assign %arg1 : i32 to %0 : i32

%1 = "emitc.member_of_ptr" (%arg0) {member = "b"} : (!emitc.ptr<!emitc.opaque<"mystruct">>) -> i32
%2 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> i32
emitc.assign %1 : i32 to %2 : i32

return
}

// CPP-DEFAULT: void member_of_pointer(mystruct* [[V0:[^ ]*]], int32_t [[V1:[^ ]*]]) {
// CPP-DEFAULT-NEXT: [[V0:[^ ]*]]->a = [[V1:[^ ]*]];
// CPP-DEFAULT-NEXT: int32_t [[V2:[^ ]*]];
// CPP-DEFAULT-NEXT: [[V2:[^ ]*]] = [[V0:[^ ]*]]->b;

0 comments on commit a5cf99d

Please sign in to comment.