[Standard] Add `std.assert` operation

We would like to add an std.assert operation with single Boolean operand and a message attribute to the standard dialect.
The operation will not have a pre-defined lowering but instead the operation can be used to hook-in a custom lowering propagating errors to a runtime.

See https://reviews.llvm.org/D83117

1 Like

+1 Both, an std.assert and an std.assume I believe have been missing.

I think it is important to flesh out a bit more the semantics if you’d like this in the “std” dialect: in particular in term of execution behavior. For example is the execution guaranteed to abort? Can we reorder these asserts? Etc.

I feel this should always abort on failure (although the revision doesn’t specify this). And to be consistent with that, reordering of such asserts would be disallowed since they would be seen as having a side-effect on the “external execution environment” or at least on input/output given the error message.

An std.assume could be used if the intention is to not abort but make use of that information for better optimization and imply undefined behavior if the condition doesn’t hold true. Reordering assume's should be possible because they would have a different kind of side effect that’d allow commuting (and even CSE but not DCE).

So, we will clarify the documentation to ensure that std.assert always aborts the execution.
The error message is a required attribute for the operation but what it is used for is still up to the user.

Should we implement a default lowering to llvm.trap or would that be difficult to replace later?

+1 for for having an assert op.

Sounds good to me. I’m not sure what the right LLVM mapping for this will be - but trap sounds good or we could make a call to assert() itself.

I would leave this unimplemented. It is very runtime specific where this should go. Calling assert() assumes a libc like target.

We already assume malloc and free are present. I don’t think that assuming so is assert is an issue. Since it’s a pattern, specific lowering pipelines can choose to ignore it, but I wouldn’t leave it unimplemented.

I updated the CL to reflect the outcome of this discussion so far.

I’m on the same line on this.

Unfortunately, assert is a C macro and its implementation varies widely across execution environments. There’s no assert symbol available across C-like execution environments that we can just emit an LLVM call to the way we do for malloc/free.

This is from a typical Linux man page, and given this, it would be reasonable to emit an assert with an integer argument?

POSIX.1-2001, POSIX.1-2008, C89, C99.  In C89, expression is required to be of type int and undefined behavior results if  it  is  not, but in C99 it may have any scalar type.

The man page also states

This macro can help programmers find bugs in their programs, or handle exceptional cases via a crash that will produce limited debugging output.

So it is indeed a macro and not something we can call directly as we do with free and malloc. We would need a wrapper library that defined a linkable symbol that contains a call to the assert macro. This is what @_sean_silva was referring to.

So, I still think we should not ship a default lowering but instead have users customize this to their own runtime needs.

The implementation of that macro that I found delegates to __assert_fail which we can call. The standard lowering could then realize the assertion test and delegate only in case of failure (if we want that).

Lowering std.assert to if(cond) abort(); seems like an OK default?

I think you won’t get the file name, line number, and the text this way, but looks good as the minimal thing to start with.

Here is the CL implementing the proposed default lowering.