mirror of https://github.com/llvm/torch-mlir
NFC: Prefactor some basicpy ops in advance of more type work.
* Organizes the BasicPyOps.td file by function. * Renamed `to_boolean` -> `as_predicate_value` (trying to consistently use "predicate" to refer to i1/low-level types and Bool/Boolean to refer to Python bool types).pull/128/head
parent
b0623b7793
commit
bea0af419d
|
@ -90,61 +90,19 @@ def CompareOperationAttr : StrEnumAttr<
|
|||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Operations
|
||||
// Constant and constructor operations
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def Basicpy_BinaryCompareOp : Basicpy_Op<"binary_compare", []> {
|
||||
let summary = "Performs a comparison between two operands";
|
||||
let description = [{
|
||||
This op performs only one step of a potentially multi-step short
|
||||
circuit comparison.
|
||||
See: https://docs.python.org/3/reference/expressions.html#comparisons
|
||||
}];
|
||||
let arguments = (ins
|
||||
AnyType:$left,
|
||||
AnyType:$right,
|
||||
CompareOperationAttr:$operation
|
||||
);
|
||||
let results = (outs
|
||||
Basicpy_BoolType:$result
|
||||
);
|
||||
let assemblyFormat = "$left $operation $right attr-dict `:` type(operands)";
|
||||
}
|
||||
|
||||
def Basicpy_BinaryExprOp : Basicpy_Op<"binary_expr", []> {
|
||||
let summary = "Binary expression";
|
||||
let description = [{
|
||||
An expression between two operands as generated by the AST BinOp node.
|
||||
}];
|
||||
let arguments = (ins
|
||||
AnyType:$left,
|
||||
AnyType:$right,
|
||||
BinaryOperationAttr:$operation
|
||||
);
|
||||
let results = (outs
|
||||
AnyType:$result
|
||||
);
|
||||
let assemblyFormat = "$left $operation $right attr-dict `:` functional-type(operands, results)";
|
||||
}
|
||||
|
||||
def Basicpy_BoolCastOp : Basicpy_Op<"bool_cast", [NoSideEffect]> {
|
||||
let summary = "Casts between BoolType and i1";
|
||||
let description = [{
|
||||
When interfacing with lower level dialect or progressively lowering
|
||||
the Python BoolType away, it is often necessary to cast between it and
|
||||
i1, which is used to represent bool-ness at lower levels.
|
||||
}];
|
||||
let arguments = (ins BoolOrI1Type:$operand);
|
||||
let results = (outs BoolOrI1Type:$result);
|
||||
let assemblyFormat = "$operand attr-dict `:` type(operands) `->` type(results)";
|
||||
}
|
||||
|
||||
def Basicpy_BoolConstantOp : Basicpy_Op<"bool_constant", [
|
||||
ConstantLike, NoSideEffect]> {
|
||||
let summary = "A boolean constant";
|
||||
let description = [{
|
||||
A constant of type !basicpy.BoolType that can take either an i1 value
|
||||
of 0 (False) or 1 (True).
|
||||
|
||||
Note that as in Python a BoolType can be thought of as an object, whereas
|
||||
the corresponding i1 is a numeric type suitable for use in contexts where
|
||||
storage format matters (or for interop with lower level dialects).
|
||||
}];
|
||||
let arguments = (ins I1Attr:$value);
|
||||
let results = (outs
|
||||
|
@ -230,6 +188,115 @@ def Basicpy_BytesConstantOp : Basicpy_Op<"bytes_constant", [
|
|||
let hasFolder = 1;
|
||||
}
|
||||
|
||||
def Basicpy_SingletonOp : Basicpy_Op<"singleton", [
|
||||
ConstantLike, NoSideEffect]> {
|
||||
let summary = "Constant value for a singleton type";
|
||||
let description = [{
|
||||
Some types only have a single possible value, represented by the
|
||||
SingletonAttr. This op allows creating constants of these types.
|
||||
}];
|
||||
let arguments = (ins);
|
||||
let results = (outs
|
||||
Basicpy_SingletonType:$result
|
||||
);
|
||||
let assemblyFormat = "attr-dict `:` type($result)";
|
||||
let hasFolder = 1;
|
||||
}
|
||||
|
||||
def Basicpy_StrConstantOp : Basicpy_Op<"str_constant", [
|
||||
ConstantLike, NoSideEffect]> {
|
||||
let summary = "Constant string value";
|
||||
let description = [{
|
||||
A string value of StrType. The value is represented by a StringAttr
|
||||
that is UTF-8 encoded.
|
||||
}];
|
||||
let arguments = (ins
|
||||
StrAttr:$value
|
||||
);
|
||||
let results = (outs
|
||||
Basicpy_StrType:$result
|
||||
);
|
||||
let assemblyFormat = "$value attr-dict";
|
||||
let hasFolder = 1;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Casting and coercion operations
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def Basicpy_AsPredicateValueOp : Basicpy_Op<"as_predicate_value",
|
||||
[NoSideEffect]> {
|
||||
let summary = "Evaluates an input to an i1 predicate value";
|
||||
let description = [{
|
||||
Applies the rules for interpreting a type as a boolean, returning an i1
|
||||
indicating the truthiness of the operand. Since the output of this op
|
||||
is intended to drive lower-level control flow, the i1 type is used (not
|
||||
the user level BoolType).
|
||||
}];
|
||||
let arguments = (ins AnyType:$operand);
|
||||
let results = (outs I1:$result);
|
||||
let assemblyFormat = "$operand attr-dict `:` type($operand)";
|
||||
}
|
||||
|
||||
def Basicpy_BoolCastOp : Basicpy_Op<"bool_cast", [NoSideEffect]> {
|
||||
let summary = "Casts between BoolType and i1 (predicate value)";
|
||||
let description = [{
|
||||
When interfacing with lower level dialect or progressively lowering
|
||||
the Python BoolType away, it is often necessary to cast between it and
|
||||
i1, which is used to represent bool-ness at lower levels.
|
||||
}];
|
||||
let arguments = (ins BoolOrI1Type:$operand);
|
||||
let results = (outs BoolOrI1Type:$result);
|
||||
let assemblyFormat = "$operand attr-dict `:` type(operands) `->` type(results)";
|
||||
}
|
||||
|
||||
def Basicpy_UnknownCastOp : Basicpy_Op<"unknown_cast", [NoSideEffect]> {
|
||||
let summary = "Casts to and from the UnknownType";
|
||||
let arguments = (ins AnyType:$operand);
|
||||
let results = (outs AnyType:$result);
|
||||
let assemblyFormat = "operands attr-dict `:` type(operands) `->` type(results)";
|
||||
|
||||
let hasCanonicalizer = 1;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Operations
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def Basicpy_BinaryCompareOp : Basicpy_Op<"binary_compare", []> {
|
||||
let summary = "Performs a comparison between two operands";
|
||||
let description = [{
|
||||
This op performs only one step of a potentially multi-step short
|
||||
circuit comparison.
|
||||
See: https://docs.python.org/3/reference/expressions.html#comparisons
|
||||
}];
|
||||
let arguments = (ins
|
||||
AnyType:$left,
|
||||
AnyType:$right,
|
||||
CompareOperationAttr:$operation
|
||||
);
|
||||
let results = (outs
|
||||
Basicpy_BoolType:$result
|
||||
);
|
||||
let assemblyFormat = "$left $operation $right attr-dict `:` type(operands)";
|
||||
}
|
||||
|
||||
def Basicpy_BinaryExprOp : Basicpy_Op<"binary_expr", []> {
|
||||
let summary = "Binary expression";
|
||||
let description = [{
|
||||
An expression between two operands as generated by the AST BinOp node.
|
||||
}];
|
||||
let arguments = (ins
|
||||
AnyType:$left,
|
||||
AnyType:$right,
|
||||
BinaryOperationAttr:$operation
|
||||
);
|
||||
let results = (outs
|
||||
AnyType:$result
|
||||
);
|
||||
let assemblyFormat = "$left $operation $right attr-dict `:` functional-type(operands, results)";
|
||||
}
|
||||
|
||||
def Basicpy_ExecOp : Basicpy_Op<"exec", [
|
||||
SingleBlockImplicitTerminator<"ExecDiscardOp">]> {
|
||||
let summary = "Evaluates an expression being executed as a statement";
|
||||
|
@ -427,58 +494,4 @@ def Basicpy_SlotObjectGetOp : Basicpy_Op<"slot_object_get", [
|
|||
);
|
||||
}
|
||||
|
||||
def Basicpy_StrConstantOp : Basicpy_Op<"str_constant", [
|
||||
ConstantLike, NoSideEffect]> {
|
||||
let summary = "Constant string value";
|
||||
let description = [{
|
||||
A string value of StrType. The value is represented by a StringAttr
|
||||
that is UTF-8 encoded.
|
||||
}];
|
||||
let arguments = (ins
|
||||
StrAttr:$value
|
||||
);
|
||||
let results = (outs
|
||||
Basicpy_StrType:$result
|
||||
);
|
||||
let assemblyFormat = "$value attr-dict";
|
||||
let hasFolder = 1;
|
||||
}
|
||||
|
||||
def Basicpy_SingletonOp : Basicpy_Op<"singleton", [
|
||||
ConstantLike, NoSideEffect]> {
|
||||
let summary = "Constant value for a singleton type";
|
||||
let description = [{
|
||||
Some types only have a single possible value, represented by the
|
||||
SingletonAttr. This op allows creating constants of these types.
|
||||
}];
|
||||
let arguments = (ins);
|
||||
let results = (outs
|
||||
Basicpy_SingletonType:$result
|
||||
);
|
||||
let assemblyFormat = "attr-dict `:` type($result)";
|
||||
let hasFolder = 1;
|
||||
}
|
||||
|
||||
def Basicpy_ToBooleanOp : Basicpy_Op<"to_boolean", [NoSideEffect]> {
|
||||
let summary = "Evaluates an input to an i1 boolean value";
|
||||
let description = [{
|
||||
Applies the rules for interpreting a type as a boolean, returning an i1
|
||||
indicating the truthiness of the operand. Since the output of this op
|
||||
is intended to drive lower-level control flow, the i1 type is used (not
|
||||
the user level BoolType).
|
||||
}];
|
||||
let arguments = (ins AnyType:$operand);
|
||||
let results = (outs I1:$result);
|
||||
let assemblyFormat = "$operand attr-dict `:` type($operand)";
|
||||
}
|
||||
|
||||
def Basicpy_UnknownCastOp : Basicpy_Op<"unknown_cast", [NoSideEffect]> {
|
||||
let summary = "Casts to and from the UnknownType";
|
||||
let arguments = (ins AnyType:$operand);
|
||||
let results = (outs AnyType:$result);
|
||||
let assemblyFormat = "operands attr-dict `:` type(operands) `->` type(results)";
|
||||
|
||||
let hasCanonicalizer = 1;
|
||||
}
|
||||
|
||||
#endif // NPCOMP_DIALECT_BASICPY_IR_BASICPY_OPS
|
||||
|
|
|
@ -215,11 +215,11 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// Converts the to_boolean op for numeric types.
|
||||
class NumericToBoolean : public OpRewritePattern<Basicpy::ToBooleanOp> {
|
||||
// Converts the as_predicate_value op for numeric types.
|
||||
class NumericToPredicateValue : public OpRewritePattern<Basicpy::AsPredicateValueOp> {
|
||||
public:
|
||||
using OpRewritePattern::OpRewritePattern;
|
||||
LogicalResult matchAndRewrite(Basicpy::ToBooleanOp op,
|
||||
LogicalResult matchAndRewrite(Basicpy::AsPredicateValueOp op,
|
||||
PatternRewriter &rewriter) const override {
|
||||
auto loc = op.getLoc();
|
||||
auto operandType = op.operand().getType();
|
||||
|
@ -245,5 +245,5 @@ void mlir::NPCOMP::populateBasicpyToStdPrimitiveOpPatterns(
|
|||
MLIRContext *context, OwningRewritePatternList &patterns) {
|
||||
patterns.insert<NumericBinaryExpr>(context);
|
||||
patterns.insert<NumericCompare>(context);
|
||||
patterns.insert<NumericToBoolean>(context);
|
||||
patterns.insert<NumericToPredicateValue>(context);
|
||||
}
|
||||
|
|
|
@ -342,7 +342,7 @@ public:
|
|||
op);
|
||||
return WalkResult::advance();
|
||||
}
|
||||
if (auto op = dyn_cast<ToBooleanOp>(childOp)) {
|
||||
if (auto op = dyn_cast<AsPredicateValueOp>(childOp)) {
|
||||
// Note that the result is always i1 and not subject to type
|
||||
// inference.
|
||||
equations.getTypeNode(op.operand());
|
||||
|
|
|
@ -140,7 +140,7 @@ public:
|
|||
// addSubtypeConstraint(op.false_value(), op.true_value(), op);
|
||||
return WalkResult::advance();
|
||||
}
|
||||
if (auto op = dyn_cast<ToBooleanOp>(childOp)) {
|
||||
if (auto op = dyn_cast<AsPredicateValueOp>(childOp)) {
|
||||
// Note that the result is always i1 and not subject to type
|
||||
// inference.
|
||||
resolveValueType(op.operand());
|
||||
|
|
|
@ -255,7 +255,7 @@ class ExpressionImporter(BaseNodeVisitor):
|
|||
next_value = self.sub_evaluate(next_node)
|
||||
if not next_nodes:
|
||||
return next_value
|
||||
condition_value = ir_h.basicpy_to_boolean_op(next_value).result
|
||||
condition_value = ir_h.basicpy_as_predicate_value_op(next_value).result
|
||||
if_op, then_ip, else_ip = ir_h.scf_if_op([ir_h.basicpy_UnknownType],
|
||||
condition_value, True)
|
||||
orig_ip = ir_h.builder.insertion_point
|
||||
|
@ -347,7 +347,7 @@ class ExpressionImporter(BaseNodeVisitor):
|
|||
|
||||
def visit_IfExp(self, ast_node):
|
||||
ir_h = self.fctx.ir_h
|
||||
test_result = ir_h.basicpy_to_boolean_op(self.sub_evaluate(
|
||||
test_result = ir_h.basicpy_as_predicate_value_op(self.sub_evaluate(
|
||||
ast_node.test)).result
|
||||
if_op, then_ip, else_ip = ir_h.scf_if_op([ir_h.basicpy_UnknownType],
|
||||
test_result, True)
|
||||
|
@ -386,7 +386,7 @@ class ExpressionImporter(BaseNodeVisitor):
|
|||
operand_value = self.sub_evaluate(ast_node.operand)
|
||||
if isinstance(op, ast.Not):
|
||||
# Special handling for logical-not.
|
||||
condition_value = ir_h.basicpy_to_boolean_op(operand_value).result
|
||||
condition_value = ir_h.basicpy_as_predicate_value_op(operand_value).result
|
||||
true_value = ir_h.basicpy_bool_constant_op(True).result
|
||||
false_value = ir_h.basicpy_bool_constant_op(False).result
|
||||
self.value = ir_h.select_op(condition_value, false_value,
|
||||
|
|
|
@ -90,8 +90,8 @@ class DialectHelper(_BaseDialectHelper):
|
|||
attrs = c.dictionary_attr({"value": c.string_attr(value.encode("utf-8"))})
|
||||
return self.op("basicpy.str_constant", [self.basicpy_StrType], [], attrs)
|
||||
|
||||
def basicpy_to_boolean_op(self, value):
|
||||
return self.op("basicpy.to_boolean", [self.i1_type], [value])
|
||||
def basicpy_as_predicate_value_op(self, value):
|
||||
return self.op("basicpy.as_predicate_value", [self.i1_type], [value])
|
||||
|
||||
def basicpy_unknown_cast_op(self, result_type, operand):
|
||||
return self.op("basicpy.unknown_cast", [result_type], [operand])
|
||||
|
|
|
@ -14,9 +14,9 @@ def logical_and():
|
|||
x = 1
|
||||
y = 0
|
||||
z = 2
|
||||
# CHECK: %[[XBOOL:.*]] = basicpy.to_boolean %[[X]]
|
||||
# CHECK: %[[XBOOL:.*]] = basicpy.as_predicate_value %[[X]]
|
||||
# CHECK: %[[IF0:.*]] = scf.if %[[XBOOL]] -> (!basicpy.UnknownType) {
|
||||
# CHECK: %[[YBOOL:.*]] = basicpy.to_boolean %[[Y]]
|
||||
# CHECK: %[[YBOOL:.*]] = basicpy.as_predicate_value %[[Y]]
|
||||
# CHECK: %[[IF1:.*]] = scf.if %[[YBOOL]] -> (!basicpy.UnknownType) {
|
||||
# CHECK: %[[ZCAST:.*]] = basicpy.unknown_cast %[[Z]]
|
||||
# CHECK: scf.yield %[[ZCAST]]
|
||||
|
@ -39,12 +39,12 @@ def logical_or():
|
|||
# CHECK: %[[X:.*]] = constant 0
|
||||
# CHECK: %[[Y:.*]] = constant 1
|
||||
# CHECK: %[[Z:.*]] = constant 2
|
||||
# CHECK: %[[XBOOL:.*]] = basicpy.to_boolean %[[X]]
|
||||
# CHECK: %[[XBOOL:.*]] = basicpy.as_predicate_value %[[X]]
|
||||
# CHECK: %[[IF0:.*]] = scf.if %[[XBOOL]] -> (!basicpy.UnknownType) {
|
||||
# CHECK: %[[XCAST:.*]] = basicpy.unknown_cast %[[X]]
|
||||
# CHECK: scf.yield %[[XCAST]]
|
||||
# CHECK: } else {
|
||||
# CHECK: %[[YBOOL:.*]] = basicpy.to_boolean %[[Y]]
|
||||
# CHECK: %[[YBOOL:.*]] = basicpy.as_predicate_value %[[Y]]
|
||||
# CHECK: %[[IF1:.*]] = scf.if %[[YBOOL]] -> (!basicpy.UnknownType) {
|
||||
# CHECK: %[[YCAST:.*]] = basicpy.unknown_cast %[[Y]]
|
||||
# CHECK: scf.yield %[[YCAST]]
|
||||
|
@ -68,7 +68,7 @@ def logical_not():
|
|||
x = 1
|
||||
# CHECK-DAG: %[[TRUE:.*]] = basicpy.bool_constant true
|
||||
# CHECK-DAG: %[[FALSE:.*]] = basicpy.bool_constant false
|
||||
# CHECK-DAG: %[[CONDITION:.*]] = basicpy.to_boolean %[[X]]
|
||||
# CHECK-DAG: %[[CONDITION:.*]] = basicpy.as_predicate_value %[[X]]
|
||||
# CHECK-DAG: %{{.*}} = select %[[CONDITION]], %[[FALSE]], %[[TRUE]] : !basicpy.BoolType
|
||||
return not x
|
||||
|
||||
|
@ -78,7 +78,7 @@ def logical_not():
|
|||
def conditional():
|
||||
# CHECK: %[[X:.*]] = constant 1
|
||||
x = 1
|
||||
# CHECK: %[[CONDITION:.*]] = basicpy.to_boolean %[[X]]
|
||||
# CHECK: %[[CONDITION:.*]] = basicpy.as_predicate_value %[[X]]
|
||||
# CHECK: %[[IF0:.*]] = scf.if %[[CONDITION]] -> (!basicpy.UnknownType) {
|
||||
# CHECK: %[[TWO:.*]] = constant 2 : i64
|
||||
# CHECK: %[[TWO_CAST:.*]] = basicpy.unknown_cast %[[TWO]]
|
||||
|
|
Loading…
Reference in New Issue