IRBuilder.if_else() and IRBuilder.if_then() don't seem to work

Not sure if this belongs to llvmlite channel or the main numba channel, but it seems like IRBuilder.if_then and IRBuilder.if_else aren’t working.

I am trying to write a very simple numba intrinsic function that will return 1 if the supplied value x is greater than 0 or return 1 otherwise. The intrinsic function looks like below code snippet, it’s called check_value():

@intrinsic
def check_value(typingctx, src):
    # create the expected type signature
    result_type = types.Integer('int64')
    sig = result_type(types.int64)
    # defines the custom code generation
    def codegen(context, builder, signature, args):
        # llvm IRBuilder code here
        gtz = builder.icmp_signed(">", args[0], args[0].type(0))
        with builder.if_else(gtz) as (then, otherwise):
            with then:
                print("gtz = True")
                a = context.get_constant(signature.args[0], 1)
            with otherwise:
                print("gtz = False")
                a = context.get_constant(signature.args[0], 0)
        return a
    return sig, codegen

and the function to be njitted looks like this:

@njit('int64(int64)')
def func(x):
    y = check_value(x)
    return y

and this is how it’s been used:

a = 10
b = func(a)
print("b =", b)

and when I run it, I am getting this output:

gtz = True
gtz = False
b = 0

But I was expecting this output:

gtz = True
b = 1

Why is it hitting both branches? i.e. printing both gtz = True and gtz = False statements? and why it is not returning 1?

Even it doesn’t work with the else case:

a = -10
b = func(a)
print("b =", b)

the output is same like when a = 10:

gtz = True
gtz = False
b = 0

How do I make it work?

Hi @chudur-budur

I think there is a misunderstanding. As the name implies, codegen is a function that generates code, the LLVM intermediate representation (IR). So both print statements are expected to be executed, since all the code generation code must be executed to generate the entire IR.

Since the otherwise case is executed last (at compile time), a will always be a constant with the value 0, which you have already observed. If you want to generate code that branches at runtime, you can do:

@extending.intrinsic
def check_value(typingctx, src):
    def codegen(context, builder, sig, args):
        llty = context.get_value_type(sig.args[0])
        
        gtz = builder.icmp_signed(">", args[0], llty(0))
        with builder.if_else(gtz) as (then, otherwise):
            with then:
                then_blk = builder.block
            with otherwise:
                otherwise_blk = builder.block
     
        ret = builder.phi(llty)
        ret.add_incoming(llty(1), then_blk)
        ret.add_incoming(llty(0), otherwise_blk)
        return ret
    
    return src(src), codegen

I hope that makes sense. Takes a bit of patience to get used to the idea of code generation.

1 Like

Thanks, it works now.

But what should I do for debugging? Let’s say I want to do some conditional printing inside then and otherwise block?

You can find a good example on that here.

1 Like