Add overflow flags support to the following ops: * `arith.addi` * `arith.subi` * `arith.muli` Example of new syntax: ``` %res = arith.addi %arg1, %arg2 overflow<nsw> : i64 ``` Similar to existing LLVM dialect syntax ``` %res = llvm.add %arg1, %arg2 overflow<nsw> : i64 ``` Tablegen canonicalization patterns updated to always drop flags, proper support with tests will be added later. Updated LLVMIR translation as part of this commit as it currenly written in a way that it will crash when new attributes added to arith ops otherwise. Also lower `arith` overflow flags to corresponding SPIR-V op decorations Discussion https://discourse.llvm.org/t/rfc-integer-overflow-flags-support-in-arith-dialect/76025 This effectively rolls forward #77211, #77700 and #77714 while adding a test to ensure the Python usage is not broken. More follow up needed but unrelated to the core change here. The changes here are minimal and just correspond to "textual namespacing" ODS side, no C++ or Python changes were needed. --------- --------- Co-authored-by: Ivan Butygin <ivan.butygin@gmail.com>, Yi Wu <yi.wu2@arm.com>
225 lines
5.1 KiB
Python
225 lines
5.1 KiB
Python
# RUN: %PYTHON %s | FileCheck %s
|
|
|
|
import gc
|
|
from mlir.ir import *
|
|
|
|
|
|
def run(f):
|
|
print("\nTEST:", f.__name__)
|
|
f()
|
|
gc.collect()
|
|
assert Context._get_live_count() == 0
|
|
return f
|
|
|
|
|
|
@run
|
|
def testLifecycleContextDestroy():
|
|
ctx = Context()
|
|
|
|
def callback(foo):
|
|
...
|
|
|
|
handler = ctx.attach_diagnostic_handler(callback)
|
|
assert handler.attached
|
|
# If context is destroyed before the handler, it should auto-detach.
|
|
ctx = None
|
|
gc.collect()
|
|
assert not handler.attached
|
|
|
|
# And finally collecting the handler should be fine.
|
|
handler = None
|
|
gc.collect()
|
|
|
|
|
|
@run
|
|
def testLifecycleExplicitDetach():
|
|
ctx = Context()
|
|
|
|
def callback(foo):
|
|
...
|
|
|
|
handler = ctx.attach_diagnostic_handler(callback)
|
|
assert handler.attached
|
|
handler.detach()
|
|
assert not handler.attached
|
|
|
|
|
|
@run
|
|
def testLifecycleWith():
|
|
ctx = Context()
|
|
|
|
def callback(foo):
|
|
...
|
|
|
|
with ctx.attach_diagnostic_handler(callback) as handler:
|
|
assert handler.attached
|
|
assert not handler.attached
|
|
|
|
|
|
@run
|
|
def testLifecycleWithAndExplicitDetach():
|
|
ctx = Context()
|
|
|
|
def callback(foo):
|
|
...
|
|
|
|
with ctx.attach_diagnostic_handler(callback) as handler:
|
|
assert handler.attached
|
|
handler.detach()
|
|
assert not handler.attached
|
|
|
|
|
|
# CHECK-LABEL: TEST: testDiagnosticCallback
|
|
@run
|
|
def testDiagnosticCallback():
|
|
ctx = Context()
|
|
|
|
def callback(d):
|
|
# CHECK: DIAGNOSTIC: message='foobar', severity=DiagnosticSeverity.ERROR, loc=loc(unknown)
|
|
print(
|
|
f"DIAGNOSTIC: message='{d.message}', severity={d.severity}, loc={d.location}"
|
|
)
|
|
return True
|
|
|
|
handler = ctx.attach_diagnostic_handler(callback)
|
|
loc = Location.unknown(ctx)
|
|
loc.emit_error("foobar")
|
|
assert not handler.had_error
|
|
|
|
|
|
# CHECK-LABEL: TEST: testDiagnosticEmptyNotes
|
|
# TODO: Come up with a way to inject a diagnostic with notes from this API.
|
|
@run
|
|
def testDiagnosticEmptyNotes():
|
|
ctx = Context()
|
|
|
|
def callback(d):
|
|
# CHECK: DIAGNOSTIC: notes=()
|
|
print(f"DIAGNOSTIC: notes={d.notes}")
|
|
return True
|
|
|
|
handler = ctx.attach_diagnostic_handler(callback)
|
|
loc = Location.unknown(ctx)
|
|
loc.emit_error("foobar")
|
|
assert not handler.had_error
|
|
|
|
|
|
# CHECK-LABEL: TEST: testDiagnosticNonEmptyNotes
|
|
@run
|
|
def testDiagnosticNonEmptyNotes():
|
|
ctx = Context()
|
|
ctx.emit_error_diagnostics = True
|
|
|
|
def callback(d):
|
|
# CHECK: DIAGNOSTIC:
|
|
# CHECK: message='arith.addi' op requires one result
|
|
# CHECK: notes=['see current operation: "arith.addi"() {{.*}} : () -> ()']
|
|
print(f"DIAGNOSTIC:")
|
|
print(f" message={d.message}")
|
|
print(f" notes={list(map(str, d.notes))}")
|
|
return True
|
|
|
|
handler = ctx.attach_diagnostic_handler(callback)
|
|
loc = Location.unknown(ctx)
|
|
try:
|
|
Operation.create("arith.addi", loc=loc).verify()
|
|
except MLIRError:
|
|
pass
|
|
assert not handler.had_error
|
|
|
|
|
|
# CHECK-LABEL: TEST: testDiagnosticCallbackException
|
|
@run
|
|
def testDiagnosticCallbackException():
|
|
ctx = Context()
|
|
|
|
def callback(d):
|
|
raise ValueError("Error in handler")
|
|
|
|
handler = ctx.attach_diagnostic_handler(callback)
|
|
loc = Location.unknown(ctx)
|
|
loc.emit_error("foobar")
|
|
assert handler.had_error
|
|
|
|
|
|
# CHECK-LABEL: TEST: testEscapingDiagnostic
|
|
@run
|
|
def testEscapingDiagnostic():
|
|
ctx = Context()
|
|
diags = []
|
|
|
|
def callback(d):
|
|
diags.append(d)
|
|
return True
|
|
|
|
handler = ctx.attach_diagnostic_handler(callback)
|
|
loc = Location.unknown(ctx)
|
|
loc.emit_error("foobar")
|
|
assert not handler.had_error
|
|
|
|
# CHECK: DIAGNOSTIC: <Invalid Diagnostic>
|
|
print(f"DIAGNOSTIC: {str(diags[0])}")
|
|
try:
|
|
diags[0].severity
|
|
raise RuntimeError("expected exception")
|
|
except ValueError:
|
|
pass
|
|
try:
|
|
diags[0].location
|
|
raise RuntimeError("expected exception")
|
|
except ValueError:
|
|
pass
|
|
try:
|
|
diags[0].message
|
|
raise RuntimeError("expected exception")
|
|
except ValueError:
|
|
pass
|
|
try:
|
|
diags[0].notes
|
|
raise RuntimeError("expected exception")
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
# CHECK-LABEL: TEST: testDiagnosticReturnTrueHandles
|
|
@run
|
|
def testDiagnosticReturnTrueHandles():
|
|
ctx = Context()
|
|
|
|
def callback1(d):
|
|
print(f"CALLBACK1: {d}")
|
|
return True
|
|
|
|
def callback2(d):
|
|
print(f"CALLBACK2: {d}")
|
|
return True
|
|
|
|
ctx.attach_diagnostic_handler(callback1)
|
|
ctx.attach_diagnostic_handler(callback2)
|
|
loc = Location.unknown(ctx)
|
|
# CHECK-NOT: CALLBACK1
|
|
# CHECK: CALLBACK2: foobar
|
|
# CHECK-NOT: CALLBACK1
|
|
loc.emit_error("foobar")
|
|
|
|
|
|
# CHECK-LABEL: TEST: testDiagnosticReturnFalseDoesNotHandle
|
|
@run
|
|
def testDiagnosticReturnFalseDoesNotHandle():
|
|
ctx = Context()
|
|
|
|
def callback1(d):
|
|
print(f"CALLBACK1: {d}")
|
|
return True
|
|
|
|
def callback2(d):
|
|
print(f"CALLBACK2: {d}")
|
|
return False
|
|
|
|
ctx.attach_diagnostic_handler(callback1)
|
|
ctx.attach_diagnostic_handler(callback2)
|
|
loc = Location.unknown(ctx)
|
|
# CHECK: CALLBACK2: foobar
|
|
# CHECK: CALLBACK1: foobar
|
|
loc.emit_error("foobar")
|