Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 46 additions & 0 deletions include/aster/Dialect/AMDGCN/IR/AMDGCNAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,52 @@ def GridDimArgAttr : AMDGCN_Attr<"GridDimArg", "grid_dim_arg", [
}];
}

//===----------------------------------------------------------------------===//
// OpaqueModifier attribute
//===----------------------------------------------------------------------===//

/// AMDGCN opaque modifier attribute.
def OpaqueModifierAttr : AMDGCN_Attr<"OpaqueModifier", "opaque_modifier"> {
let summary = "AMDGCN opaque modifier attribute";
let description = [{
Attribute representing an opaque modifier with a string value.

Example:

```mlir
#amdgcn.opaque_modifier<"neg">
```
}];
let parameters = (ins StringRefParameter<>:$value);
let assemblyFormat = "`<` $value `>`";
}

//===----------------------------------------------------------------------===//
// ModifiersAttr attribute
//===----------------------------------------------------------------------===//

def ModifiersAttr
: ArrayOfAttr<AMDGCN_Dialect, "Modifiers", "modifiers",
"::mlir::aster::amdgcn::OpaqueModifierAttr"> {
let summary = "AMDGCN modifiers list attribute";
let description = [{
Attribute representing a list of opaque modifiers.

Example:

```mlir
#amdgcn.modifiers<[#amdgcn.opaque_modifier<"neg">, #amdgcn.opaque_modifier<"abs">]>
```
}];
let constBuilderCall = [{
$_builder.getAttr<ModifiersAttr>(ArrayRef<OpaqueModifierAttr>($0))
}];
let defaultValue = "{}";
let assemblyFormat = [{
`<`(`[` $value^ `]`)? `>`
}];
}

//===----------------------------------------------------------------------===//
// Kernel arguments attribute
//===----------------------------------------------------------------------===//
Expand Down
60 changes: 60 additions & 0 deletions include/aster/Dialect/AMDGCN/IR/AMDGCNOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,66 @@ def AMDGCN_WaitOp : AMDGCN_Op<"wait", [
let hasCanonicalizeMethod = 1;
}

//===----------------------------------------------------------------------===//
// OpaqueOp
//===----------------------------------------------------------------------===//

def AMDGCN_OpaqueOp : AMDGCN_Op<"opaque", [
AMDGCNInstOpInterface,
AttrSizedOperandSegments
]> {
let summary = "AMDGCN opaque instruction";
let description = [{
An opaque instruction that can represent any AMDGCN instruction.
This is useful for representing instructions that are not yet fully
modeled in the dialect.

The `mnemonic` attribute specifies the instruction mnemonic.
The `in_mask` is a dense bool array where `true` signifies the
operand appears in the operand list and `false` that the operand is off.
If all values in `in_mask` are `true`, it is omitted from the assembly.

Example:
```mlir
%result = amdgcn.opaque "v_add_f32" outs(%dst) ins(%src0, %src1)
: (!amdgcn.vgpr<10>, !amdgcn.vgpr<11>, !amdgcn.vgpr<12>) -> !amdgcn.vgpr<10>
```
}];
let arguments = (ins
StrAttr:$mnemonic,
Variadic<AnyType>:$outs,
Variadic<AnyType>:$ins,
DenseBoolArrayAttr:$in_mask,
OptionalAttr<ModifiersAttr>:$modifiers
);
let results = (outs Variadic<AnyType>:$results);
let assemblyFormat = [{
$mnemonic (`outs` `(` $outs^ `)`)? (`ins` `(` $ins^ `)`)?
custom<OpaqueInsMask>(ref($ins), $in_mask) custom<OpaqueModifiers>($modifiers)
attr-dict `:` functional-type(operands, results)
}];
let hasVerifier = 1;
let extraClassDeclaration = [{
//===------------------------------------------------------------------===//
// InstOpInterface Methods
//===------------------------------------------------------------------===//

/// Get the opcode of the instruction.
InstAttr getOpcodeAttr() {
// Opaque ops don't have a typed opcode, return null.
return InstAttr();
}
/// Get the instruction output operands.
MutableArrayRef<OpOperand> getInstOutsMutable() {
return getOutsMutable();
}
/// Get the instruction input operands.
MutableArrayRef<OpOperand> getInstInsMutable() {
return getInsMutable();
}
}];
}

//===----------------------------------------------------------------------===//
// AMDGCN Target Operations
//===----------------------------------------------------------------------===//
Expand Down
105 changes: 105 additions & 0 deletions lib/Dialect/AMDGCN/IR/AMDGCN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,111 @@ LogicalResult LibraryOp::verify() {
[&]() { return emitError(); });
}

//===----------------------------------------------------------------------===//
// OpaqueOp
//===----------------------------------------------------------------------===//

/// Check if all values in the mask are true.
static bool isAllTrue(ArrayRef<bool> mask) {
return llvm::all_of(mask, [](bool v) { return v; });
}

/// Parse the optional ins_mask for OpaqueOp.
/// Format: (`ins_mask` $in_mask)?
/// If not present, creates an all-true mask with size matching the ins
/// operands.
static ParseResult
parseOpaqueInsMask(OpAsmParser &parser,
ArrayRef<OpAsmParser::UnresolvedOperand> ins,
DenseBoolArrayAttr &inMask) {
// Try to parse the optional ins_mask keyword.
if (succeeded(parser.parseOptionalKeyword("ins_mask"))) {
if (parser.parseAttribute(inMask))
return failure();
} else {
// No ins_mask provided, create a default mask of all true.
SmallVector<bool> defaultMask(ins.size(), true);
inMask = DenseBoolArrayAttr::get(parser.getContext(), defaultMask);
}
return success();
}

/// Print the optional ins_mask for OpaqueOp.
/// Only prints if the mask is not all true.
static void printOpaqueInsMask(OpAsmPrinter &printer, Operation *op,
OperandRange ins, DenseBoolArrayAttr inMask) {
ArrayRef<bool> mask = inMask.asArrayRef();
if (!isAllTrue(mask)) {
printer << " ins_mask ";
printer.printAttribute(inMask);
}
}

/// Parse the optional modifiers for OpaqueOp.
/// Format: `modifiers` `(` $mod (`,` $mod)* `)`
static ParseResult parseOpaqueModifiers(OpAsmParser &parser,
ModifiersAttr &modifiers) {
// Try to parse the optional modifiers keyword.
if (failed(parser.parseOptionalKeyword("modifiers")))
return success();

// Parse the opening parenthesis.
if (parser.parseLParen())
return failure();

// Parse the list of modifiers.
SmallVector<OpaqueModifierAttr> modList;
do {
std::string modStr;
if (parser.parseKeywordOrString(&modStr))
return failure();
modList.push_back(OpaqueModifierAttr::get(parser.getContext(), modStr));
} while (succeeded(parser.parseOptionalComma()));

// Parse the closing parenthesis.
if (parser.parseRParen())
return failure();

modifiers = ModifiersAttr::get(parser.getContext(), modList);
return success();
}

/// Print the optional modifiers for OpaqueOp.
/// Format: `modifiers` `(` $mod (`,` $mod)* `)`
static void printOpaqueModifiers(OpAsmPrinter &printer, Operation *op,
ModifiersAttr modifiers) {
if (!modifiers || modifiers.empty())
return;

printer << " modifiers(";
llvm::interleaveComma(modifiers.getValue(), printer, [&](Attribute attr) {
auto mod = cast<OpaqueModifierAttr>(attr);
printer << mod.getValue();
});
printer << ")";
}

LogicalResult OpaqueOp::verify() {
// Check that all outs are AMDGCN register types.
for (Value out : getOuts()) {
if (!isa<AMDGCNRegisterTypeInterface>(out.getType()))
return emitOpError("expected all 'outs' operands to be AMDGCN register "
"types, but got ")
<< out.getType();
}

// Check that the number of true elements in in_mask matches the number of
// ins operands.
ArrayRef<bool> mask = getInMask();
size_t numTrue = llvm::count(mask, true);
if (numTrue != getIns().size())
return emitOpError("expected the number of 'true' elements in 'in_mask' (")
<< numTrue << ") to match the number of 'ins' operands ("
<< getIns().size() << ")";

return success();
}

//===----------------------------------------------------------------------===//
// WaitOp
//===----------------------------------------------------------------------===//
Expand Down
36 changes: 36 additions & 0 deletions lib/Target/ASM/TranslateModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,42 @@ static void printLSOffset(amdgcn::AsmPrinter &printer, AMDGCNInstOpInterface op,
/// Prints the given instruction using the AsmPrinter.
static llvm::LogicalResult printInstruction(amdgcn::AsmPrinter &printer,
AMDGCNInstOpInterface op) {
if (auto opaque = dyn_cast<OpaqueOp>(op.getOperation())) {
// Print: mnemonic outs ins modifiers
auto guard = printer.printMnemonic(opaque.getMnemonic());

// Print outs operands.
llvm::interleave(
opaque.getOuts(), printer.getStream(),
[&](Value out) { printer.printOperand(out); }, ",");

bool requiresComma = !opaque.getOuts().empty();
// Print ins operands - check in_mask: if true print operand, else "off".
int64_t insIdx = 0;
for (bool inMask : opaque.getInMask()) {
if (requiresComma)
printer.getStream() << ",";
requiresComma = true;
if (inMask) {
// Print the actual operand.
printer.printOperand(opaque.getIns()[insIdx++]);
continue;
}
printer.printKeyword("off");
}

// Print modifiers - each entry in the array literally.
if (std::optional<ArrayRef<OpaqueModifierAttr>> modifiers =
opaque.getModifiers();
modifiers && !modifiers->empty()) {
printer.getStream() << " ";
auto printMod = [&](OpaqueModifierAttr mod) {
printer.getStream() << mod.getValue();
};
llvm::interleave(*modifiers, printer.getStream(), printMod, " ");
}
return success();
}
OpCode opcode = op.getOpcodeAttr().getValue();
assert(opcode != OpCode::Invalid && "invalid opcode in instruction");
if (_instPrinters.size() <= static_cast<size_t>(opcode) ||
Expand Down
28 changes: 28 additions & 0 deletions test/Dialect/AMDGCN/invalid.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,31 @@ func.func @offset_with_invalid_aligment() {
%2 = amdgcn.alloc_lds 32 alignment 8 offset 33
return
}

// -----

//===----------------------------------------------------------------------===//
// OpaqueOp Verification
//===----------------------------------------------------------------------===//

func.func @opaque_invalid_outs_type(%arg0: i32, %arg1: !amdgcn.vgpr<2>, %arg2: !amdgcn.vgpr<3>) -> i32 {
// expected-error @+1 {{expected all 'outs' operands to be AMDGCN register types, but got 'i32'}}
%0 = amdgcn.opaque "v_add_f32" outs(%arg0) ins(%arg1, %arg2) : (i32, !amdgcn.vgpr<2>, !amdgcn.vgpr<3>) -> i32
return %0 : i32
}

// -----

func.func @opaque_in_mask_mismatch(%arg0: !amdgcn.vgpr<1>, %arg1: !amdgcn.vgpr<2>) -> !amdgcn.vgpr<1> {
// expected-error @+1 {{expected the number of 'true' elements in 'in_mask' (2) to match the number of 'ins' operands (1)}}
%0 = amdgcn.opaque "v_add_f32" outs(%arg0) ins(%arg1) ins_mask array<i1: true, true, false> : (!amdgcn.vgpr<1>, !amdgcn.vgpr<2>) -> !amdgcn.vgpr<1>
return %0 : !amdgcn.vgpr<1>
}

// -----

func.func @opaque_in_mask_mismatch_zero(%arg0: !amdgcn.vgpr<1>, %arg1: !amdgcn.vgpr<2>, %arg2: !amdgcn.vgpr<3>) -> !amdgcn.vgpr<1> {
// expected-error @+1 {{expected the number of 'true' elements in 'in_mask' (0) to match the number of 'ins' operands (2)}}
%0 = amdgcn.opaque "v_add_f32" outs(%arg0) ins(%arg1, %arg2) ins_mask array<i1: false, false> : (!amdgcn.vgpr<1>, !amdgcn.vgpr<2>, !amdgcn.vgpr<3>) -> !amdgcn.vgpr<1>
return %0 : !amdgcn.vgpr<1>
}
34 changes: 34 additions & 0 deletions test/Dialect/AMDGCN/ops.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,37 @@ func.func @lds_buffer_ops(%arg0: index, %arg1: index) {
%2 = amdgcn.alloc_lds 32 offset 64
return
}

//===----------------------------------------------------------------------===//
// OpaqueOp Operations
//===----------------------------------------------------------------------===//

func.func @opaque_basic(%arg0: !amdgcn.vgpr<1>, %arg1: !amdgcn.vgpr<2>, %arg2: !amdgcn.vgpr<3>) -> !amdgcn.vgpr<1> {
%0 = amdgcn.opaque "v_add_f32" outs(%arg0) ins(%arg1, %arg2) modifiers("offset(0)", neg) : (!amdgcn.vgpr<1>, !amdgcn.vgpr<2>, !amdgcn.vgpr<3>) -> !amdgcn.vgpr<1>
return %0 : !amdgcn.vgpr<1>
}

func.func @opaque_no_outs(%arg0: !amdgcn.vgpr<1>, %arg1: !amdgcn.vgpr<2>) {
amdgcn.opaque "v_mov_b32" ins(%arg0, %arg1) : (!amdgcn.vgpr<1>, !amdgcn.vgpr<2>) -> ()
return
}

func.func @opaque_no_ins(%arg0: !amdgcn.vgpr<1>) -> !amdgcn.vgpr<1> {
%0 = amdgcn.opaque "v_nop" outs(%arg0) : (!amdgcn.vgpr<1>) -> !amdgcn.vgpr<1>
return %0 : !amdgcn.vgpr<1>
}

func.func @opaque_with_ins_mask(%arg0: !amdgcn.vgpr<1>, %arg1: !amdgcn.vgpr<2>) -> !amdgcn.vgpr<1> {
%0 = amdgcn.opaque "v_add_f32" outs(%arg0) ins(%arg1) ins_mask array<i1: true, false> : (!amdgcn.vgpr<1>, !amdgcn.vgpr<2>) -> !amdgcn.vgpr<1>
return %0 : !amdgcn.vgpr<1>
}

func.func @opaque_multiple_results(%arg0: !amdgcn.vgpr<1>, %arg1: !amdgcn.vgpr<2>) -> (!amdgcn.vgpr<1>) {
%0 = amdgcn.opaque "v_div_scale_f32" outs(%arg0) ins(%arg1) : (!amdgcn.vgpr<1>, !amdgcn.vgpr<2>) -> (!amdgcn.vgpr<1>)
return %0 : !amdgcn.vgpr<1>
}

func.func @opaque_sgpr_and_vgpr(%arg0: !amdgcn.sgpr<1>, %arg1: !amdgcn.vgpr<2>, %arg2: !amdgcn.vgpr<3>) -> !amdgcn.sgpr<1> {
%0 = amdgcn.opaque "v_add_f32" outs(%arg0) ins(%arg1, %arg2) : (!amdgcn.sgpr<1>, !amdgcn.vgpr<2>, !amdgcn.vgpr<3>) -> !amdgcn.sgpr<1>
return %0 : !amdgcn.sgpr<1>
}
15 changes: 15 additions & 0 deletions test/Target/ASM/opaque.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: aster-translate %s --mlir-to-asm | FileCheck %s

// CHECK-LABEL:Module: mod
// CHECK: v_add_f32 v3, v2, v3
// CHECK: v_add_f32 v3, v2, v3 offset(0) neg
amdgcn.module @mod target = #amdgcn.target<gfx942> isa = #amdgcn.isa<cdna3> {
amdgcn.kernel @opaque {
%0 = amdgcn.alloca : !amdgcn.vgpr<2>
%1 = amdgcn.alloca : !amdgcn.vgpr<3>
%2 = amdgcn.alloca : !amdgcn.vgpr<3>
opaque "v_add_f32" outs(%2) ins(%0, %1) : (!amdgcn.vgpr<3>, !amdgcn.vgpr<2>, !amdgcn.vgpr<3>) -> (!amdgcn.vgpr<3>)
opaque "v_add_f32" outs(%2) ins(%0, %1) modifiers("offset(0)", neg) : (!amdgcn.vgpr<3>, !amdgcn.vgpr<2>, !amdgcn.vgpr<3>) -> (!amdgcn.vgpr<3>)
amdgcn.end_kernel
}
}