Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
79 changes: 79 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1702,6 +1702,47 @@ def CIR_BinOp : CIR_Op<"binop", [
}];
}

//===----------------------------------------------------------------------===//
// BinOpOverflow
//===----------------------------------------------------------------------===//

def CIR_BinOpOverflow : CIR_Op<"binop.overflow", [Pure]> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't needed for this PR. Even if you were adding handlers for both sets of builtins, that would need to be done in separate PRs. We have a policy of keeping PRs focused on a single feature or bug fix.

let summary = "Binary operations with overflow detection";
let description = [{
cir.binop.overflow performs the binary operation according to
the specified opcode kind (add, sub, or mul) and returns both
the result and an overflow flag.

It requires two input operands and returns two results:
- The result of the operation (same type as operands)
- An overflow flag (boolean type)

The operation supports both signed and unsigned integer types.
For signed types, overflow is detected when the result cannot
be represented in the result type. For unsigned types, overflow
is detected when the result wraps around.

```mlir
%result, %overflow = cir.binop.overflow(add, %1, %2) : !s32i -> (!s32i, !bool)
%result, %overflow = cir.binop.overflow(sub, %3, %4) : !u32i -> (!u32i, !bool)
%result, %overflow = cir.binop.overflow(mul, %5, %6) : !s64i -> (!s64i, !bool)
```
}];

let arguments = (ins
CIR_BinOpKind:$kind,
CIR_AnyType:$lhs, CIR_AnyType:$rhs
);

let results = (outs CIR_AnyType:$result, CIR_BoolType:$overflow);

let assemblyFormat = [{
`(` $kind `,` $lhs `,` $rhs `)` `:` type($lhs) `->` `(` type($result) `,` type($overflow) `)` attr-dict
}];

let hasVerifier = 1;
}

//===----------------------------------------------------------------------===//
// ShiftOp
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -4052,6 +4093,44 @@ def CIR_ExpectOp : CIR_Op<"expect", [
}];
}

//===----------------------------------------------------------------------===//
// IsConstantOp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did you choose the placement of this? We generally try to keep things in the same relative order as they are in the incubator to help with comparisons.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for mentioning the incubator ! I hadn’t come across that detail earlier, but I appreciate you pointing it out. I’ll go through it and make sure I’m aligned before moving forward.

//===----------------------------------------------------------------------===//

def CIR_IsConstantOp : CIR_Op<"is_constant", [Pure]> {
let summary = "Check if a value is a compile-time constant";
let description = [{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great expansion of the incubator description. Thanks!

The `cir.is_constant` operation checks whether its input value is a
compile-time constant. This operation models the `__builtin_constant_p`
builtin function.

The operation takes a single operand of any CIR type and returns a signed
32-bit integer. The result is 1 if the operand is a compile-time constant,
and 0 otherwise.

If the value can be determined to be constant at compile time, this
operation may be folded to a constant value. Otherwise, it will be lowered
to the `llvm.is.constant` intrinsic.

Example:

```mlir
%0 = cir.is_constant %expr : i32 -> !s32i
%1 = cir.is_constant %ptr : !cir.ptr<i32> -> !s32i
```
}];

let arguments = (ins CIR_AnyType:$value);
let results = (outs CIR_SInt32:$result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let results = (outs CIR_SInt32:$result);
let results = (outs CIR_BoolType:$result);

Why did you change this from what is in the incubator?


let assemblyFormat = [{
$value `:` type($value) `->` type($result) attr-dict
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xlauko Is this consistent with the style we're using now. I know you've been updating things recently, but I don't have a good sense for the expected style.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andykaylor pretty much yes. I am traying us to converge on style : argument types -> return types respectivelly : type if the operation can be determined by a single type. Bit of style guide is in: https://llvm.github.io/clangir/Dialect/cir-style-guide.html

}];

let hasFolder = 1;
let hasLLVMLowering = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be lowered to LLVM IR. The lowering is simple enough to include in this PR.

}

//===----------------------------------------------------------------------===//
// PrefetchOp
//===----------------------------------------------------------------------===//
Expand Down
25 changes: 25 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,31 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
return RValue::get(
builder.createBitcast(allocaAddr, builder.getVoidPtrTy()));
}

case Builtin::BI__builtin_constant_p: {
auto loc = getLoc(e->getSourceRange());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto loc = getLoc(e->getSourceRange());
mlir::Location loc = getLoc(e->getSourceRange());

Upstream LLVM uses auto less liberally than the incubator.

https://llvm.org/docs/CodingStandards.html#use-auto-type-deduction-to-make-code-more-readable


Expr::EvalResult evalResult;

// Try to evaluate at compile time first
if (e->getArg(0)->EvaluateAsRValue(evalResult, getContext()) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've diverged from both the incubator and classic codegen here. That's OK if your changes improve the code while keeping the same semantics, but I don't think what you've done here meets those criteria. We need this to behave exactly like classic codegen. Please use the code from the incubator.

!evalResult.hasSideEffects()) {
// Expression is a compile-time constant, return 1
llvm::APInt apInt(32, 1);
llvm::APSInt apSInt(apInt, /*isUnsigned=*/false);
return RValue::get(builder.getConstInt(loc, apSInt));
}

// Expression cannot be evaluated at compile time, emit runtime check
mlir::Value argValue = emitScalarExpr(e->getArg(0));
mlir::Type resultType = builder.getSInt32Ty();
auto isConstantOp = cir::IsConstantOp::create(builder, loc, resultType, argValue);
mlir::Value resultValue = isConstantOp.getResult();
mlir::Type exprTy = convertType(e->getType());
if (exprTy != resultValue.getType())
resultValue = builder.createIntCast(resultValue, exprTy);
return RValue::get(resultValue);
}

case Builtin::BIcos:
case Builtin::BIcosf:
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2117,6 +2117,25 @@ OpFoldResult cir::UnaryOp::fold(FoldAdaptor adaptor) {
return {};
}

//===----------------------------------------------------------------------===//
// IsConstantOp Definitions
//===----------------------------------------------------------------------===//

OpFoldResult cir::IsConstantOp::fold(FoldAdaptor adaptor) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Folder is turned off in the operation, please set hasFolder = 1

// If the input value is a constant attribute, return 1 (true)
mlir::Attribute value = adaptor.getValue();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested this? It doesn't look right.

if (value) {
// The value is a compile-time constant, so return 1
mlir::Type resultType = getResult().getType();
llvm::APInt apInt(32, 1);
llvm::APSInt apSInt(apInt, /*isUnsigned=*/false);
return cir::IntAttr::get(resultType, apSInt);
}

// If the input is not a constant, we cannot fold
return {};
}

//===----------------------------------------------------------------------===//
// CopyOp Definitions
//===----------------------------------------------------------------------===//
Expand Down