Skip to content

Flag --canonicalize is broken in mlir-opt with LLVM 16 #64774

@azteca1998

Description

@azteca1998

LLVM version

LLVM 16.0.6 in Linux.

MLIR input

module {
  func.func public @felt2bool(%arg0: i252) -> !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)> attributes {llvm.emit_c_interface} {
    %c1_i64 = arith.constant 1 : i64
    %0 = llvm.alloca %c1_i64 x !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)> {alignment = 1 : i64} : (i64) -> !llvm.ptr<struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>>
    %c1_i64_0 = arith.constant 1 : i64
    %1 = llvm.alloca %c1_i64_0 x !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)> {alignment = 1 : i64} : (i64) -> !llvm.ptr<struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>>
    cf.br ^bb1(%arg0 : i252)
  ^bb1(%2: i252):  // pred: ^bb0
    cf.br ^bb2(%2 : i252)
  ^bb2(%3: i252):  // pred: ^bb1
    cf.br ^bb3(%3 : i252)
  ^bb3(%4: i252):  // pred: ^bb2
    cf.br ^bb4
  ^bb4:  // pred: ^bb3
    %c1_i252 = arith.constant 1 : i252
    cf.br ^bb5(%c1_i252 : i252)
  ^bb5(%5: i252):  // pred: ^bb4
    cf.br ^bb6(%5 : i252)
  ^bb6(%6: i252):  // pred: ^bb5
    cf.br ^bb7(%3 : i252)
  ^bb7(%7: i252):  // pred: ^bb6
    cf.br ^bb8(%5 : i252)
  ^bb8(%8: i252):  // pred: ^bb7
    cf.br ^bb9(%7, %8 : i252, i252)
  ^bb9(%9: i252, %10: i252):  // pred: ^bb8
    %11 = arith.extui %9 : i252 to i256
    %12 = arith.extui %10 : i252 to i256
    %13 = arith.subi %11, %12 : i256
    %c3618502788666131213697322783095070105623107215331596699973092056135872020481_i256 = arith.constant 3618502788666131213697322783095070105623107215331596699973092056135872020481 : i256
    %14 = arith.addi %13, %c3618502788666131213697322783095070105623107215331596699973092056135872020481_i256 : i256
    %15 = arith.cmpi ult, %11, %12 : i256
    %16 = arith.select %15, %14, %13 : i256
    %17 = arith.trunci %16 : i256 to i252
    cf.br ^bb10(%17 : i252)
  ^bb10(%18: i252):  // pred: ^bb9
    cf.br ^bb11(%18 : i252)
  ^bb11(%19: i252):  // pred: ^bb10
    %c0_i252 = arith.constant 0 : i252
    %20 = arith.cmpi eq, %19, %c0_i252 : i252
    cf.cond_br %20, ^bb12, ^bb17
  ^bb12:  // pred: ^bb11
    cf.br ^bb13
  ^bb13:  // pred: ^bb12
    %21 = llvm.mlir.undef : !llvm.struct<()>
    cf.br ^bb14(%21 : !llvm.struct<()>)
  ^bb14(%22: !llvm.struct<()>):  // pred: ^bb13
    %true = arith.constant true
    %23 = llvm.mlir.undef : !llvm.struct<(i1, array<0 x i8>, struct<()>)>
    %24 = llvm.insertvalue %true, %23[0] : !llvm.struct<(i1, array<0 x i8>, struct<()>)> 
    %25 = llvm.insertvalue %22, %24[2] : !llvm.struct<(i1, array<0 x i8>, struct<()>)> 
    %26 = llvm.bitcast %1 : !llvm.ptr<struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>> to !llvm.ptr<struct<(i1, array<0 x i8>, struct<()>)>>
    llvm.store %25, %26 {alignment = 1 : i64} : !llvm.ptr<struct<(i1, array<0 x i8>, struct<()>)>>
    %27 = llvm.load %1 {alignment = 1 : i64} : !llvm.ptr<struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>>
    cf.br ^bb15(%27 : !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>)
  ^bb15(%28: !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>):  // pred: ^bb14
    cf.br ^bb16
  ^bb16:  // pred: ^bb15
    cf.br ^bb22(%28 : !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>)
  ^bb17:  // pred: ^bb11
    cf.br ^bb18(%19 : i252)
  ^bb18(%29: i252):  // pred: ^bb17
    cf.br ^bb19
  ^bb19:  // pred: ^bb18
    %30 = llvm.mlir.undef : !llvm.struct<()>
    cf.br ^bb20(%30 : !llvm.struct<()>)
  ^bb20(%31: !llvm.struct<()>):  // pred: ^bb19
    %false = arith.constant false
    %32 = llvm.mlir.undef : !llvm.struct<(i1, array<0 x i8>, struct<()>)>
    %33 = llvm.insertvalue %false, %32[0] : !llvm.struct<(i1, array<0 x i8>, struct<()>)> 
    %34 = llvm.insertvalue %31, %33[2] : !llvm.struct<(i1, array<0 x i8>, struct<()>)> 
    %35 = llvm.bitcast %0 : !llvm.ptr<struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>> to !llvm.ptr<struct<(i1, array<0 x i8>, struct<()>)>>
    llvm.store %34, %35 {alignment = 1 : i64} : !llvm.ptr<struct<(i1, array<0 x i8>, struct<()>)>>
    %36 = llvm.load %0 {alignment = 1 : i64} : !llvm.ptr<struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>>
    cf.br ^bb21(%36 : !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>)
  ^bb21(%37: !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>):  // pred: ^bb20
    cf.br ^bb22(%37 : !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>)
  ^bb22(%38: !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>):  // 2 preds: ^bb16, ^bb21
    cf.br ^bb23(%38 : !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>)
  ^bb23(%39: !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>):  // pred: ^bb22
    cf.br ^bb24(%39 : !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>)
  ^bb24(%40: !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>):  // pred: ^bb23
    return %39 : !llvm.struct<(i1, array<0 x i8>, struct<()>, array<0 x i8>)>
  }
}

Expected behaviour

When calling felt2bool::felt2bool::felt_to_bool with 0, 1, and 2 it should return false, true, false (the first value of the returned struct, aka the i1). Invoking it after conversion without canonicalization works as expected, however when adding the --canonicalize flag it returns the wrong value when the argument is 0.

In other words, for an input of 0, 1 and 2, the outputs are:

  • Expected: false, true, false (works correctly without --canonicalize).
  • Actual: false, true, false (actual behaviour with --canonicalize).

Steps to reproduce

Write the following piece of code in felt2bool.c:

#include <stdint.h>
#include <stdio.h>


void _mlir_ciface_felt2bool(uint8_t *dst, uint64_t, uint64_t, uint64_t, uint64_t);


int main()
{
    size_t i;
    uint8_t dst;

    for (i = 0; i < 3; i++)
    {
        _mlir_ciface_felt2bool(&dst, i, 0, 0, 0);
        printf("felt2bool(%lu) = %s\n", i, dst ? "true" : "false");
    }

    return 0;
}

Store the MLIR code as felt2bool.mlir. Then, compile the source code into LLVM IR both with and without --canonicalize:

cat felt2bool.mlir | mlir-opt-16 --convert-scf-to-cf --convert-func-to-llvm | mlir-translate-16 --mlir-to-llvmir > felt2bool-ok.ll
cat felt2bool.mlir | mlir-opt-16 --canonicalize --convert-scf-to-cf --convert-func-to-llvm | mlir-translate-16 --mlir-to-llvmir > felt2bool-err.ll

Build binaries for both versions:

clang-16 felt2bool.c felt2bool-ok.ll -o felt2bool-ok
clang-16 felt2bool.c felt2bool-err.ll -o felt2bool-err

Execute them:

./felt2bool-ok
./felt2bool-err

The output will be as follows:

$ ./felt2bool-ok 
felt2bool(0) = false
felt2bool(1) = true
felt2bool(2) = false
$ ./felt2bool-err 
felt2bool(0) = true
felt2bool(1) = true
felt2bool(2) = false

The felt2bool-err's felt2bool(0) should print false to be correct.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions