We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Tensor.where() bug on LLVM
when either `input_` or `other` is of `dtypes.bool`, llvmir attempts to turn bool into float: `%".16" = fpext i1 %".15" to float` but this raises 
```
RuntimeError: LLVM IR parsing error
<string>:19:18: error: invalid cast opcode for cast from 'i1' to 'float'
```

to reproduce:
```
from tinygrad.lazy import Device
from tinygrad.tensor import Tensor
from tinygrad.helpers import dtypes
Device.DEFAULT = "LLVM"
cond = Tensor([0,1]) == Tensor([1,1]);
cond.where(Tensor([1,1], dtype=dtypes.bool), 0).realize()
```

prg:
```
; ModuleID = "/Users/zibo/fun/tinygrad/tinygrad/renderer/llvmir.py"                                                                              
target triple = "unknown-unknown-unknown"
target datalayout = ""                                                                                                                           
                                                                                                                                                 define void @"E_2n1"(float* %".1", float* %".2", float* %".3", float* %".4", i1* %".5") "no-nans-fp-math"="true"
{                                                                       
entry:
  br label %"loop_body_gidx0"                                                                                                                    
loop_body_gidx0:                   
  %"gidx0" = phi  i64 [0, %"entry"], [%".23", %"loop_body_gidx0"] 
  %".8" = getelementptr inbounds float, float* %".2", i64 %"gidx0"                                                                               
  %".9" = load float, float* %".8"                                      
  %".10" = getelementptr inbounds float, float* %".3", i64 %"gidx0"
  %".11" = load float, float* %".10" 
  %".12" = getelementptr inbounds float, float* %".4", i64 0                                                                                     
  %".13" = load float, float* %".12"                                                                                                             
  %".14" = getelementptr inbounds i1, i1* %".5", i64 %"gidx0"
  %".15" = load i1, i1* %".14"   
  %".16" = fpext i1 %".15" to float   # HERE IT IS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  %".17" = fcmp fast oeq float %".9", %".11"                            
  %".18" = uitofp i1 %".17" to float 
  %".19" = fcmp fast une float %".18",              0x0                                                                                          
  %".20" = select fast i1 %".19", float %".13", float %".16"           
  %".21" = getelementptr inbounds float, float* %".1", i64 %"gidx0"                                                                              
  store float %".20", float* %".21"                                                                                                              
  %".23" = add i64 %"gidx0", 1
  %".24" = icmp eq i64 %".23", 2                                                                                                                 
  br i1 %".24", label %"loop_exit_gidx0", label %"loop_body_gidx0"
loop_exit_gidx0:                                                        
  ret void           
}
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `tinygrad/renderer/llvmir.py`
Content:
```
1 from typing import Final, Dict, Callable, Any, List, Optional, Tuple
2 import functools
3 from llvmlite import ir  # type: ignore
4 from tinygrad.codegen.linearizer import UOps, UOp, Token, MemOp, ConstOp
5 from tinygrad.helpers import dtypes
6 from tinygrad.ops import Op, UnaryOps, BinaryOps, TernaryOps
7 
8 from tinygrad.shape.symbolic import Variable, NumNode, MulNode, DivNode, ModNode, LtNode, SumNode, AndNode
9 def int_const(x): return ir.Constant(ir.IntType(64), x)
10 render_llvm = {
11   NumNode: lambda self,ops,ctx: int_const(self.b),
12   MulNode: lambda self,ops,ctx: ctx.mul(self.a.render(ops,ctx), int_const(self.b)),
13   DivNode: lambda self,ops,ctx: ctx.sdiv(self.a.render(ops,ctx), int_const(self.b)),
14   ModNode: lambda self,ops,ctx: ctx.srem(self.a.render(ops,ctx), int_const(self.b)),
15   LtNode: lambda self,ops,ctx: ctx.icmp_signed("<", self.a.render(ops,ctx), int_const(self.b)),
16   SumNode: lambda self,ops,ctx: functools.reduce(lambda a,b: ctx.add(a,b.render(ops,ctx)), self.nodes[1:], self.nodes[0].render(ops,ctx)),
17   AndNode: lambda self,ops,ctx: functools.reduce(lambda a,b: ctx.and_(a,b.render(ops,ctx)), self.nodes[1:], self.nodes[0].render(ops,ctx))
18 }
19 
20 code_for_op: Final[Dict[Op, Callable]] = {
21   UnaryOps.EXP2: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.exp2', [ir.FloatType()]), [x], fastmath=('fast',)),
22   UnaryOps.LOG2: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.log2', [ir.FloatType()]), [x], fastmath=('fast',)),
23   UnaryOps.SIN: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.sin', [ir.FloatType()]), [x], fastmath=('fast',)),
24   UnaryOps.SQRT: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.sqrt', [ir.FloatType()]), [x], fastmath=('fast',)),
25   BinaryOps.ADD: lambda builder,x,y: builder.fadd(x,y, flags=('fast',)),
26   BinaryOps.SUB: lambda builder,x,y: builder.fsub(x,y, flags=('fast',)),
27   BinaryOps.MUL: lambda builder,x,y: builder.fmul(x,y, flags=('fast',)),
28   BinaryOps.DIV: lambda builder,x,y: builder.fdiv(x,y, flags=('fast',)),
29   BinaryOps.CMPEQ: lambda builder,x,y: builder.uitofp(builder.fcmp_ordered("==", x, y, flags=('fast',)), ir.FloatType()),
30   BinaryOps.MAX: lambda builder,x,y: builder.select(builder.fcmp_unordered(">", x, y, flags=('fast',)), x, y, flags=('fast',)),
31   TernaryOps.MULACC: lambda builder,x,y,z: builder.fadd(builder.fmul(x,y, flags=('fast',)), z, flags=('fast',)),
32   TernaryOps.WHERE: lambda builder,x,y,z: builder.select(builder.fcmp_unordered("!=", x, ir.Constant(ir.FloatType(), 0), flags=('fast',)), y, z, flags=('fast',)),
33 }
34 
35 def uops_to_llvm_ir(function_name:str, uops:List[UOp]) -> Tuple[str, Optional[List[int]], Optional[List[int]]]:
36   # all llvm stuff goes into a module
37   module = ir.Module(name=__file__)
38 
39   # extract global buffers
40   buf_to_dtype = {args[0]:args[1] for uop,_,_,args in uops if uop == UOps.DEFINE_GLOBAL}
41   buf_index = {x:i for i,x in enumerate(buf_to_dtype.keys())}
42 
43   # create llvm function
44   dtype_to_llvm_dtype = {dtypes.float16:ir.HalfType(), dtypes.bfloat16:ir.IntType(16), dtypes.float32:ir.FloatType(), dtypes.int8:ir.IntType(8), dtypes.uint8:ir.IntType(8), dtypes.bool: ir.IntType(1), dtypes.int64: ir.IntType(64), dtypes.int32: ir.IntType(32)}
45   func_dtypes = [dtype_to_llvm_dtype[dtype] for dtype in buf_to_dtype.values()]
46   func = ir.Function(module, ir.FunctionType(ir.VoidType(), [x.as_pointer() for x in func_dtypes]), name=function_name)
47 
48   # force llvmlite to allow us to add function attribute then add the attribute
49   func.attributes._known = func.attributes._known.union(frozenset(['"no-nans-fp-math"="true"']))
50   func.attributes.add('"no-nans-fp-math"="true"')
51 
52   bb = [ir.IRBuilder(func.append_basic_block("entry"))]
53   loop_blocks = []
54   reduce_phis: List = []
55   # TODO: newvar probably shouldn't be optional
56   lvars: Dict[Optional[Token], Any] = {}  # this Any is an llvm type
57   render_llvm[Variable] = lambda self,ops,ctx: lvars[self.expr]
58 
59   for uop,newvar,vin,args in uops:
60     if uop == UOps.LOOP:
61       for var in args[0]:
62         if isinstance(var, NumNode): continue
63         bb.append(ir.IRBuilder(func.append_basic_block(f"loop_body_{var.expr}")))
64         bb[-2].branch(bb[-1]._block)
65 
66         phis = []
67         for rp in reduce_phis:
68           incoming = lvars[rp]
69           lvars[rp] = bb[-1].phi(ir.FloatType())
70           lvars[rp].add_incoming(incoming, bb[-2]._block)
71           phis.append((rp, lvars[rp]))
72         loop_blocks.append((bb[-1], phis))
73 
74         lvars[var.expr] = bb[-1].phi(ir.IntType(64), name=var.expr)
75         lvars[var.expr].add_incoming(int_const(var.min), bb[-2]._block)
76     if uop == UOps.ENDLOOP:
77       for var in args[0][::-1]:
78         if isinstance(var, NumNode): continue
79         block, phis = loop_blocks.pop()
80         idx_p1 = bb[-1].add(lvars[var.expr], int_const(1))
81         lvars[var.expr].add_incoming(idx_p1, bb[-1]._block)
82         for n,phi in phis: phi.add_incoming(lvars[n], bb[-1]._block)
83         bb.append(ir.IRBuilder(func.append_basic_block(f"loop_exit_{var.expr}")))
84         bb[-2].cbranch(bb[-2].icmp_unsigned("==", idx_p1, int_const(var.max+1)), bb[-1]._block, block._block)
85     if uop == UOps.LOAD:
86       assert newvar is not None and isinstance(args, (MemOp, ConstOp))
87       assert newvar.dtype == dtypes.float, "newvar must be float"
88       valid = args.valid.render(render_llvm, bb[-1])
89       if isinstance(args, ConstOp):
90         if args.valid.min == 0 and args.valid.max == 1:
91           val = bb[-1].select(valid, ir.Constant(ir.FloatType(), args.value), ir.Constant(ir.FloatType(), args.invalid_value))
92         else:
93           val = ir.Constant(ir.FloatType(), args.value if args.valid.min == 1 else args.invalid_value)
94         # TODO: this is a hack. it shouldn't be const that signals this
95         reduce_phis.append(newvar)
96       else:
97         idx = args.idx.render(render_llvm, bb[-1])
98         if args.valid.max == 0:
99           val = ir.Constant(dtype_to_llvm_dtype[args.memory_dtype], args.invalid_value)
100         elif args.valid.min == 0:
101           aug_idx = bb[-1].select(valid, idx, int_const(0))
102           val = bb[-1].select(valid, bb[-1].load(bb[-1].gep(func.args[buf_index[args.name]], [aug_idx], inbounds=True)), ir.Constant(dtype_to_llvm_dtype[args.memory_dtype], args.invalid_value))
103         else:
104           val = bb[-1].load(bb[-1].gep(func.args[buf_index[args.name]], [idx], inbounds=True))
105 
106         if args.memory_dtype != newvar.dtype:
107           if dtypes.is_int(args.memory_dtype):
108             val = bb[-1].uitofp(val, ir.FloatType()) if dtypes.is_unsigned(args.memory_dtype) else bb[-1].sitofp(val, ir.FloatType())
109           elif args.memory_dtype == dtypes.bfloat16:
110             val = bb[-1].sext(val, ir.IntType(32))
111             val = bb[-1].shl(val, ir.Constant(ir.IntType(32), 16))
112             val = bb[-1].bitcast(val, ir.FloatType())
113           else:
114             val = bb[-1].fpext(val, ir.FloatType())
115       lvars[newvar] = val
116     if uop == UOps.STORE:
117       assert args.valid.min == 1 and isinstance(args, MemOp), "store must be valid and to memory"
118       idx = args.idx.render(render_llvm, bb[-1])
119       element = lvars[vin[0]]
120       if args.memory_dtype != vin[0].dtype:
121         if dtypes.is_int(args.memory_dtype):
122           element = bb[-1].fptoui(element, dtype_to_llvm_dtype[args.memory_dtype]) if dtypes.is_unsigned(args.memory_dtype) else bb[-1].fptosi(element, dtype_to_llvm_dtype[args.memory_dtype])
123         elif args.memory_dtype == dtypes.bfloat16:
124           element = bb[-1].bitcast(element, ir.IntType(32))
125           element = bb[-1].lshr(element, ir.Constant(ir.IntType(32), 16))
126           element = bb[-1].trunc(element, ir.IntType(16))
127         else:
128           element = bb[-1].fptrunc(element, dtype_to_llvm_dtype[args.memory_dtype])
129       bb[-1].store(element, bb[-1].gep(func.args[buf_index[args.name]], [idx], inbounds=True))
130     if uop == UOps.ALU:
131       lvars[newvar] = code_for_op[args](bb[-1], *[lvars[x] for x in vin])
132 
133   bb[-1].ret_void()
134   return str(module), None, None
135
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/tinygrad/renderer/llvmir.py b/tinygrad/renderer/llvmir.py
--- a/tinygrad/renderer/llvmir.py
+++ b/tinygrad/renderer/llvmir.py
@@ -104,8 +104,8 @@
           val = bb[-1].load(bb[-1].gep(func.args[buf_index[args.name]], [idx], inbounds=True))
 
         if args.memory_dtype != newvar.dtype:
-          if dtypes.is_int(args.memory_dtype):
-            val = bb[-1].uitofp(val, ir.FloatType()) if dtypes.is_unsigned(args.memory_dtype) else bb[-1].sitofp(val, ir.FloatType())
+          if dtypes.is_int(args.memory_dtype) or args.memory_dtype == dtypes.bool:
+            val = bb[-1].uitofp(val, ir.FloatType()) if dtypes.is_unsigned(args.memory_dtype) or args.memory_dtype == dtypes.bool else bb[-1].sitofp(val, ir.FloatType())
           elif args.memory_dtype == dtypes.bfloat16:
             val = bb[-1].sext(val, ir.IntType(32))
             val = bb[-1].shl(val, ir.Constant(ir.IntType(32), 16))

{"golden_diff": "diff --git a/tinygrad/renderer/llvmir.py b/tinygrad/renderer/llvmir.py\n--- a/tinygrad/renderer/llvmir.py\n+++ b/tinygrad/renderer/llvmir.py\n@@ -104,8 +104,8 @@\n           val = bb[-1].load(bb[-1].gep(func.args[buf_index[args.name]], [idx], inbounds=True))\n \n         if args.memory_dtype != newvar.dtype:\n-          if dtypes.is_int(args.memory_dtype):\n-            val = bb[-1].uitofp(val, ir.FloatType()) if dtypes.is_unsigned(args.memory_dtype) else bb[-1].sitofp(val, ir.FloatType())\n+          if dtypes.is_int(args.memory_dtype) or args.memory_dtype == dtypes.bool:\n+            val = bb[-1].uitofp(val, ir.FloatType()) if dtypes.is_unsigned(args.memory_dtype) or args.memory_dtype == dtypes.bool else bb[-1].sitofp(val, ir.FloatType())\n           elif args.memory_dtype == dtypes.bfloat16:\n             val = bb[-1].sext(val, ir.IntType(32))\n             val = bb[-1].shl(val, ir.Constant(ir.IntType(32), 16))\n", "issue": "Tensor.where() bug on LLVM\nwhen either `input_` or `other` is of `dtypes.bool`, llvmir attempts to turn bool into float: `%\".16\" = fpext i1 %\".15\" to float` but this raises \r\n```\r\nRuntimeError: LLVM IR parsing error\r\n<string>:19:18: error: invalid cast opcode for cast from 'i1' to 'float'\r\n```\r\n\r\nto reproduce:\r\n```\r\nfrom tinygrad.lazy import Device\r\nfrom tinygrad.tensor import Tensor\r\nfrom tinygrad.helpers import dtypes\r\nDevice.DEFAULT = \"LLVM\"\r\ncond = Tensor([0,1]) == Tensor([1,1]);\r\ncond.where(Tensor([1,1], dtype=dtypes.bool), 0).realize()\r\n```\r\n\r\nprg:\r\n```\r\n; ModuleID = \"/Users/zibo/fun/tinygrad/tinygrad/renderer/llvmir.py\"                                                                              \r\ntarget triple = \"unknown-unknown-unknown\"\r\ntarget datalayout = \"\"                                                                                                                           \r\n                                                                                                                                                 define void @\"E_2n1\"(float* %\".1\", float* %\".2\", float* %\".3\", float* %\".4\", i1* %\".5\") \"no-nans-fp-math\"=\"true\"\r\n{                                                                       \r\nentry:\r\n  br label %\"loop_body_gidx0\"                                                                                                                    \r\nloop_body_gidx0:                   \r\n  %\"gidx0\" = phi  i64 [0, %\"entry\"], [%\".23\", %\"loop_body_gidx0\"] \r\n  %\".8\" = getelementptr inbounds float, float* %\".2\", i64 %\"gidx0\"                                                                               \r\n  %\".9\" = load float, float* %\".8\"                                      \r\n  %\".10\" = getelementptr inbounds float, float* %\".3\", i64 %\"gidx0\"\r\n  %\".11\" = load float, float* %\".10\" \r\n  %\".12\" = getelementptr inbounds float, float* %\".4\", i64 0                                                                                     \r\n  %\".13\" = load float, float* %\".12\"                                                                                                             \r\n  %\".14\" = getelementptr inbounds i1, i1* %\".5\", i64 %\"gidx0\"\r\n  %\".15\" = load i1, i1* %\".14\"   \r\n  %\".16\" = fpext i1 %\".15\" to float   # HERE IT IS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\r\n  %\".17\" = fcmp fast oeq float %\".9\", %\".11\"                            \r\n  %\".18\" = uitofp i1 %\".17\" to float \r\n  %\".19\" = fcmp fast une float %\".18\",              0x0                                                                                          \r\n  %\".20\" = select fast i1 %\".19\", float %\".13\", float %\".16\"           \r\n  %\".21\" = getelementptr inbounds float, float* %\".1\", i64 %\"gidx0\"                                                                              \r\n  store float %\".20\", float* %\".21\"                                                                                                              \r\n  %\".23\" = add i64 %\"gidx0\", 1\r\n  %\".24\" = icmp eq i64 %\".23\", 2                                                                                                                 \r\n  br i1 %\".24\", label %\"loop_exit_gidx0\", label %\"loop_body_gidx0\"\r\nloop_exit_gidx0:                                                        \r\n  ret void           \r\n}\r\n```\r\n\n", "before_files": [{"content": "from typing import Final, Dict, Callable, Any, List, Optional, Tuple\nimport functools\nfrom llvmlite import ir  # type: ignore\nfrom tinygrad.codegen.linearizer import UOps, UOp, Token, MemOp, ConstOp\nfrom tinygrad.helpers import dtypes\nfrom tinygrad.ops import Op, UnaryOps, BinaryOps, TernaryOps\n\nfrom tinygrad.shape.symbolic import Variable, NumNode, MulNode, DivNode, ModNode, LtNode, SumNode, AndNode\ndef int_const(x): return ir.Constant(ir.IntType(64), x)\nrender_llvm = {\n  NumNode: lambda self,ops,ctx: int_const(self.b),\n  MulNode: lambda self,ops,ctx: ctx.mul(self.a.render(ops,ctx), int_const(self.b)),\n  DivNode: lambda self,ops,ctx: ctx.sdiv(self.a.render(ops,ctx), int_const(self.b)),\n  ModNode: lambda self,ops,ctx: ctx.srem(self.a.render(ops,ctx), int_const(self.b)),\n  LtNode: lambda self,ops,ctx: ctx.icmp_signed(\"<\", self.a.render(ops,ctx), int_const(self.b)),\n  SumNode: lambda self,ops,ctx: functools.reduce(lambda a,b: ctx.add(a,b.render(ops,ctx)), self.nodes[1:], self.nodes[0].render(ops,ctx)),\n  AndNode: lambda self,ops,ctx: functools.reduce(lambda a,b: ctx.and_(a,b.render(ops,ctx)), self.nodes[1:], self.nodes[0].render(ops,ctx))\n}\n\ncode_for_op: Final[Dict[Op, Callable]] = {\n  UnaryOps.EXP2: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.exp2', [ir.FloatType()]), [x], fastmath=('fast',)),\n  UnaryOps.LOG2: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.log2', [ir.FloatType()]), [x], fastmath=('fast',)),\n  UnaryOps.SIN: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.sin', [ir.FloatType()]), [x], fastmath=('fast',)),\n  UnaryOps.SQRT: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.sqrt', [ir.FloatType()]), [x], fastmath=('fast',)),\n  BinaryOps.ADD: lambda builder,x,y: builder.fadd(x,y, flags=('fast',)),\n  BinaryOps.SUB: lambda builder,x,y: builder.fsub(x,y, flags=('fast',)),\n  BinaryOps.MUL: lambda builder,x,y: builder.fmul(x,y, flags=('fast',)),\n  BinaryOps.DIV: lambda builder,x,y: builder.fdiv(x,y, flags=('fast',)),\n  BinaryOps.CMPEQ: lambda builder,x,y: builder.uitofp(builder.fcmp_ordered(\"==\", x, y, flags=('fast',)), ir.FloatType()),\n  BinaryOps.MAX: lambda builder,x,y: builder.select(builder.fcmp_unordered(\">\", x, y, flags=('fast',)), x, y, flags=('fast',)),\n  TernaryOps.MULACC: lambda builder,x,y,z: builder.fadd(builder.fmul(x,y, flags=('fast',)), z, flags=('fast',)),\n  TernaryOps.WHERE: lambda builder,x,y,z: builder.select(builder.fcmp_unordered(\"!=\", x, ir.Constant(ir.FloatType(), 0), flags=('fast',)), y, z, flags=('fast',)),\n}\n\ndef uops_to_llvm_ir(function_name:str, uops:List[UOp]) -> Tuple[str, Optional[List[int]], Optional[List[int]]]:\n  # all llvm stuff goes into a module\n  module = ir.Module(name=__file__)\n\n  # extract global buffers\n  buf_to_dtype = {args[0]:args[1] for uop,_,_,args in uops if uop == UOps.DEFINE_GLOBAL}\n  buf_index = {x:i for i,x in enumerate(buf_to_dtype.keys())}\n\n  # create llvm function\n  dtype_to_llvm_dtype = {dtypes.float16:ir.HalfType(), dtypes.bfloat16:ir.IntType(16), dtypes.float32:ir.FloatType(), dtypes.int8:ir.IntType(8), dtypes.uint8:ir.IntType(8), dtypes.bool: ir.IntType(1), dtypes.int64: ir.IntType(64), dtypes.int32: ir.IntType(32)}\n  func_dtypes = [dtype_to_llvm_dtype[dtype] for dtype in buf_to_dtype.values()]\n  func = ir.Function(module, ir.FunctionType(ir.VoidType(), [x.as_pointer() for x in func_dtypes]), name=function_name)\n\n  # force llvmlite to allow us to add function attribute then add the attribute\n  func.attributes._known = func.attributes._known.union(frozenset(['\"no-nans-fp-math\"=\"true\"']))\n  func.attributes.add('\"no-nans-fp-math\"=\"true\"')\n\n  bb = [ir.IRBuilder(func.append_basic_block(\"entry\"))]\n  loop_blocks = []\n  reduce_phis: List = []\n  # TODO: newvar probably shouldn't be optional\n  lvars: Dict[Optional[Token], Any] = {}  # this Any is an llvm type\n  render_llvm[Variable] = lambda self,ops,ctx: lvars[self.expr]\n\n  for uop,newvar,vin,args in uops:\n    if uop == UOps.LOOP:\n      for var in args[0]:\n        if isinstance(var, NumNode): continue\n        bb.append(ir.IRBuilder(func.append_basic_block(f\"loop_body_{var.expr}\")))\n        bb[-2].branch(bb[-1]._block)\n\n        phis = []\n        for rp in reduce_phis:\n          incoming = lvars[rp]\n          lvars[rp] = bb[-1].phi(ir.FloatType())\n          lvars[rp].add_incoming(incoming, bb[-2]._block)\n          phis.append((rp, lvars[rp]))\n        loop_blocks.append((bb[-1], phis))\n\n        lvars[var.expr] = bb[-1].phi(ir.IntType(64), name=var.expr)\n        lvars[var.expr].add_incoming(int_const(var.min), bb[-2]._block)\n    if uop == UOps.ENDLOOP:\n      for var in args[0][::-1]:\n        if isinstance(var, NumNode): continue\n        block, phis = loop_blocks.pop()\n        idx_p1 = bb[-1].add(lvars[var.expr], int_const(1))\n        lvars[var.expr].add_incoming(idx_p1, bb[-1]._block)\n        for n,phi in phis: phi.add_incoming(lvars[n], bb[-1]._block)\n        bb.append(ir.IRBuilder(func.append_basic_block(f\"loop_exit_{var.expr}\")))\n        bb[-2].cbranch(bb[-2].icmp_unsigned(\"==\", idx_p1, int_const(var.max+1)), bb[-1]._block, block._block)\n    if uop == UOps.LOAD:\n      assert newvar is not None and isinstance(args, (MemOp, ConstOp))\n      assert newvar.dtype == dtypes.float, \"newvar must be float\"\n      valid = args.valid.render(render_llvm, bb[-1])\n      if isinstance(args, ConstOp):\n        if args.valid.min == 0 and args.valid.max == 1:\n          val = bb[-1].select(valid, ir.Constant(ir.FloatType(), args.value), ir.Constant(ir.FloatType(), args.invalid_value))\n        else:\n          val = ir.Constant(ir.FloatType(), args.value if args.valid.min == 1 else args.invalid_value)\n        # TODO: this is a hack. it shouldn't be const that signals this\n        reduce_phis.append(newvar)\n      else:\n        idx = args.idx.render(render_llvm, bb[-1])\n        if args.valid.max == 0:\n          val = ir.Constant(dtype_to_llvm_dtype[args.memory_dtype], args.invalid_value)\n        elif args.valid.min == 0:\n          aug_idx = bb[-1].select(valid, idx, int_const(0))\n          val = bb[-1].select(valid, bb[-1].load(bb[-1].gep(func.args[buf_index[args.name]], [aug_idx], inbounds=True)), ir.Constant(dtype_to_llvm_dtype[args.memory_dtype], args.invalid_value))\n        else:\n          val = bb[-1].load(bb[-1].gep(func.args[buf_index[args.name]], [idx], inbounds=True))\n\n        if args.memory_dtype != newvar.dtype:\n          if dtypes.is_int(args.memory_dtype):\n            val = bb[-1].uitofp(val, ir.FloatType()) if dtypes.is_unsigned(args.memory_dtype) else bb[-1].sitofp(val, ir.FloatType())\n          elif args.memory_dtype == dtypes.bfloat16:\n            val = bb[-1].sext(val, ir.IntType(32))\n            val = bb[-1].shl(val, ir.Constant(ir.IntType(32), 16))\n            val = bb[-1].bitcast(val, ir.FloatType())\n          else:\n            val = bb[-1].fpext(val, ir.FloatType())\n      lvars[newvar] = val\n    if uop == UOps.STORE:\n      assert args.valid.min == 1 and isinstance(args, MemOp), \"store must be valid and to memory\"\n      idx = args.idx.render(render_llvm, bb[-1])\n      element = lvars[vin[0]]\n      if args.memory_dtype != vin[0].dtype:\n        if dtypes.is_int(args.memory_dtype):\n          element = bb[-1].fptoui(element, dtype_to_llvm_dtype[args.memory_dtype]) if dtypes.is_unsigned(args.memory_dtype) else bb[-1].fptosi(element, dtype_to_llvm_dtype[args.memory_dtype])\n        elif args.memory_dtype == dtypes.bfloat16:\n          element = bb[-1].bitcast(element, ir.IntType(32))\n          element = bb[-1].lshr(element, ir.Constant(ir.IntType(32), 16))\n          element = bb[-1].trunc(element, ir.IntType(16))\n        else:\n          element = bb[-1].fptrunc(element, dtype_to_llvm_dtype[args.memory_dtype])\n      bb[-1].store(element, bb[-1].gep(func.args[buf_index[args.name]], [idx], inbounds=True))\n    if uop == UOps.ALU:\n      lvars[newvar] = code_for_op[args](bb[-1], *[lvars[x] for x in vin])\n\n  bb[-1].ret_void()\n  return str(module), None, None\n", "path": "tinygrad/renderer/llvmir.py"}], "after_files": [{"content": "from typing import Final, Dict, Callable, Any, List, Optional, Tuple\nimport functools\nfrom llvmlite import ir  # type: ignore\nfrom tinygrad.codegen.linearizer import UOps, UOp, Token, MemOp, ConstOp\nfrom tinygrad.helpers import dtypes\nfrom tinygrad.ops import Op, UnaryOps, BinaryOps, TernaryOps\n\nfrom tinygrad.shape.symbolic import Variable, NumNode, MulNode, DivNode, ModNode, LtNode, SumNode, AndNode\ndef int_const(x): return ir.Constant(ir.IntType(64), x)\nrender_llvm = {\n  NumNode: lambda self,ops,ctx: int_const(self.b),\n  MulNode: lambda self,ops,ctx: ctx.mul(self.a.render(ops,ctx), int_const(self.b)),\n  DivNode: lambda self,ops,ctx: ctx.sdiv(self.a.render(ops,ctx), int_const(self.b)),\n  ModNode: lambda self,ops,ctx: ctx.srem(self.a.render(ops,ctx), int_const(self.b)),\n  LtNode: lambda self,ops,ctx: ctx.icmp_signed(\"<\", self.a.render(ops,ctx), int_const(self.b)),\n  SumNode: lambda self,ops,ctx: functools.reduce(lambda a,b: ctx.add(a,b.render(ops,ctx)), self.nodes[1:], self.nodes[0].render(ops,ctx)),\n  AndNode: lambda self,ops,ctx: functools.reduce(lambda a,b: ctx.and_(a,b.render(ops,ctx)), self.nodes[1:], self.nodes[0].render(ops,ctx))\n}\n\ncode_for_op: Final[Dict[Op, Callable]] = {\n  UnaryOps.EXP2: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.exp2', [ir.FloatType()]), [x], fastmath=('fast',)),\n  UnaryOps.LOG2: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.log2', [ir.FloatType()]), [x], fastmath=('fast',)),\n  UnaryOps.SIN: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.sin', [ir.FloatType()]), [x], fastmath=('fast',)),\n  UnaryOps.SQRT: lambda builder,x: builder.call(builder._block.module.declare_intrinsic('llvm.sqrt', [ir.FloatType()]), [x], fastmath=('fast',)),\n  BinaryOps.ADD: lambda builder,x,y: builder.fadd(x,y, flags=('fast',)),\n  BinaryOps.SUB: lambda builder,x,y: builder.fsub(x,y, flags=('fast',)),\n  BinaryOps.MUL: lambda builder,x,y: builder.fmul(x,y, flags=('fast',)),\n  BinaryOps.DIV: lambda builder,x,y: builder.fdiv(x,y, flags=('fast',)),\n  BinaryOps.CMPEQ: lambda builder,x,y: builder.uitofp(builder.fcmp_ordered(\"==\", x, y, flags=('fast',)), ir.FloatType()),\n  BinaryOps.MAX: lambda builder,x,y: builder.select(builder.fcmp_unordered(\">\", x, y, flags=('fast',)), x, y, flags=('fast',)),\n  TernaryOps.MULACC: lambda builder,x,y,z: builder.fadd(builder.fmul(x,y, flags=('fast',)), z, flags=('fast',)),\n  TernaryOps.WHERE: lambda builder,x,y,z: builder.select(builder.fcmp_unordered(\"!=\", x, ir.Constant(ir.FloatType(), 0), flags=('fast',)), y, z, flags=('fast',)),\n}\n\ndef uops_to_llvm_ir(function_name:str, uops:List[UOp]) -> Tuple[str, Optional[List[int]], Optional[List[int]]]:\n  # all llvm stuff goes into a module\n  module = ir.Module(name=__file__)\n\n  # extract global buffers\n  buf_to_dtype = {args[0]:args[1] for uop,_,_,args in uops if uop == UOps.DEFINE_GLOBAL}\n  buf_index = {x:i for i,x in enumerate(buf_to_dtype.keys())}\n\n  # create llvm function\n  dtype_to_llvm_dtype = {dtypes.float16:ir.HalfType(), dtypes.bfloat16:ir.IntType(16), dtypes.float32:ir.FloatType(), dtypes.int8:ir.IntType(8), dtypes.uint8:ir.IntType(8), dtypes.bool: ir.IntType(1), dtypes.int64: ir.IntType(64), dtypes.int32: ir.IntType(32)}\n  func_dtypes = [dtype_to_llvm_dtype[dtype] for dtype in buf_to_dtype.values()]\n  func = ir.Function(module, ir.FunctionType(ir.VoidType(), [x.as_pointer() for x in func_dtypes]), name=function_name)\n\n  # force llvmlite to allow us to add function attribute then add the attribute\n  func.attributes._known = func.attributes._known.union(frozenset(['\"no-nans-fp-math\"=\"true\"']))\n  func.attributes.add('\"no-nans-fp-math\"=\"true\"')\n\n  bb = [ir.IRBuilder(func.append_basic_block(\"entry\"))]\n  loop_blocks = []\n  reduce_phis: List = []\n  # TODO: newvar probably shouldn't be optional\n  lvars: Dict[Optional[Token], Any] = {}  # this Any is an llvm type\n  render_llvm[Variable] = lambda self,ops,ctx: lvars[self.expr]\n\n  for uop,newvar,vin,args in uops:\n    if uop == UOps.LOOP:\n      for var in args[0]:\n        if isinstance(var, NumNode): continue\n        bb.append(ir.IRBuilder(func.append_basic_block(f\"loop_body_{var.expr}\")))\n        bb[-2].branch(bb[-1]._block)\n\n        phis = []\n        for rp in reduce_phis:\n          incoming = lvars[rp]\n          lvars[rp] = bb[-1].phi(ir.FloatType())\n          lvars[rp].add_incoming(incoming, bb[-2]._block)\n          phis.append((rp, lvars[rp]))\n        loop_blocks.append((bb[-1], phis))\n\n        lvars[var.expr] = bb[-1].phi(ir.IntType(64), name=var.expr)\n        lvars[var.expr].add_incoming(int_const(var.min), bb[-2]._block)\n    if uop == UOps.ENDLOOP:\n      for var in args[0][::-1]:\n        if isinstance(var, NumNode): continue\n        block, phis = loop_blocks.pop()\n        idx_p1 = bb[-1].add(lvars[var.expr], int_const(1))\n        lvars[var.expr].add_incoming(idx_p1, bb[-1]._block)\n        for n,phi in phis: phi.add_incoming(lvars[n], bb[-1]._block)\n        bb.append(ir.IRBuilder(func.append_basic_block(f\"loop_exit_{var.expr}\")))\n        bb[-2].cbranch(bb[-2].icmp_unsigned(\"==\", idx_p1, int_const(var.max+1)), bb[-1]._block, block._block)\n    if uop == UOps.LOAD:\n      assert newvar is not None and isinstance(args, (MemOp, ConstOp))\n      assert newvar.dtype == dtypes.float, \"newvar must be float\"\n      valid = args.valid.render(render_llvm, bb[-1])\n      if isinstance(args, ConstOp):\n        if args.valid.min == 0 and args.valid.max == 1:\n          val = bb[-1].select(valid, ir.Constant(ir.FloatType(), args.value), ir.Constant(ir.FloatType(), args.invalid_value))\n        else:\n          val = ir.Constant(ir.FloatType(), args.value if args.valid.min == 1 else args.invalid_value)\n        # TODO: this is a hack. it shouldn't be const that signals this\n        reduce_phis.append(newvar)\n      else:\n        idx = args.idx.render(render_llvm, bb[-1])\n        if args.valid.max == 0:\n          val = ir.Constant(dtype_to_llvm_dtype[args.memory_dtype], args.invalid_value)\n        elif args.valid.min == 0:\n          aug_idx = bb[-1].select(valid, idx, int_const(0))\n          val = bb[-1].select(valid, bb[-1].load(bb[-1].gep(func.args[buf_index[args.name]], [aug_idx], inbounds=True)), ir.Constant(dtype_to_llvm_dtype[args.memory_dtype], args.invalid_value))\n        else:\n          val = bb[-1].load(bb[-1].gep(func.args[buf_index[args.name]], [idx], inbounds=True))\n\n        if args.memory_dtype != newvar.dtype:\n          if dtypes.is_int(args.memory_dtype) or args.memory_dtype == dtypes.bool:\n            val = bb[-1].uitofp(val, ir.FloatType()) if dtypes.is_unsigned(args.memory_dtype) or args.memory_dtype == dtypes.bool else bb[-1].sitofp(val, ir.FloatType())\n          elif args.memory_dtype == dtypes.bfloat16:\n            val = bb[-1].sext(val, ir.IntType(32))\n            val = bb[-1].shl(val, ir.Constant(ir.IntType(32), 16))\n            val = bb[-1].bitcast(val, ir.FloatType())\n          else:\n            val = bb[-1].fpext(val, ir.FloatType())\n      lvars[newvar] = val\n    if uop == UOps.STORE:\n      assert args.valid.min == 1 and isinstance(args, MemOp), \"store must be valid and to memory\"\n      idx = args.idx.render(render_llvm, bb[-1])\n      element = lvars[vin[0]]\n      if args.memory_dtype != vin[0].dtype:\n        if dtypes.is_int(args.memory_dtype):\n          element = bb[-1].fptoui(element, dtype_to_llvm_dtype[args.memory_dtype]) if dtypes.is_unsigned(args.memory_dtype) else bb[-1].fptosi(element, dtype_to_llvm_dtype[args.memory_dtype])\n        elif args.memory_dtype == dtypes.bfloat16:\n          element = bb[-1].bitcast(element, ir.IntType(32))\n          element = bb[-1].lshr(element, ir.Constant(ir.IntType(32), 16))\n          element = bb[-1].trunc(element, ir.IntType(16))\n        else:\n          element = bb[-1].fptrunc(element, dtype_to_llvm_dtype[args.memory_dtype])\n      bb[-1].store(element, bb[-1].gep(func.args[buf_index[args.name]], [idx], inbounds=True))\n    if uop == UOps.ALU:\n      lvars[newvar] = code_for_op[args](bb[-1], *[lvars[x] for x in vin])\n\n  bb[-1].ret_void()\n  return str(module), None, None\n", "path": "tinygrad/renderer/llvmir.py"}]}