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
Stella Laurenzo 2020-11-23 15:33:11 -08:00
parent b0623b7793
commit bea0af419d
7 changed files with 131 additions and 118 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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());

View File

@ -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());

View File

@ -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,

View File

@ -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])

View File

@ -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]]