Quellcode durchsuchen

Replace many `foreign` llvm calls with intrinsics

gingerBill vor 4 Jahren
Ursprung
Commit
72aa0e6e38

+ 18 - 142
core/math/bits/bits.odin

@@ -1,5 +1,6 @@
 package math_bits
 
+import "intrinsics"
 import "core:runtime"
 
 U8_MIN  :: 0;
@@ -22,32 +23,10 @@ I16_MAX :: 1 << 15 - 1;
 I32_MAX :: 1 << 31 - 1;
 I64_MAX :: 1 << 63 - 1;
 
-@(default_calling_convention="none")
-foreign {
-	@(link_name="llvm.ctpop.i8")        count_ones8  :: proc(i:  u8) ->  u8 ---
-	@(link_name="llvm.ctpop.i16")       count_ones16 :: proc(i: u16) -> u16 ---
-	@(link_name="llvm.ctpop.i32")       count_ones32 :: proc(i: u32) -> u32 ---
-	@(link_name="llvm.ctpop.i64")       count_ones64 :: proc(i: u64) -> u64 ---
 
-	@(link_name="llvm.cttz.i8")         trailing_zeros8  :: proc(i:  u8,  is_zero_undef := false) ->  u8 ---
-	@(link_name="llvm.cttz.i16")        trailing_zeros16 :: proc(i: u16,  is_zero_undef := false) -> u16 ---
-	@(link_name="llvm.cttz.i32")        trailing_zeros32 :: proc(i: u32,  is_zero_undef := false) -> u32 ---
-	@(link_name="llvm.cttz.i64")        trailing_zeros64 :: proc(i: u64,  is_zero_undef := false) -> u64 ---
-
-	@(link_name="llvm.bitreverse.i8")   reverse_bits8  :: proc(i:  u8) ->  u8 ---
-	@(link_name="llvm.bitreverse.i16")  reverse_bits16 :: proc(i: u16) -> u16 ---
-	@(link_name="llvm.bitreverse.i32")  reverse_bits32 :: proc(i: u32) -> u32 ---
-	@(link_name="llvm.bitreverse.i64")  reverse_bits64 :: proc(i: u64) -> u64 ---
-}
-
-
-trailing_zeros_uint :: proc(i: uint) -> uint {
-	when size_of(uint) == size_of(u64) {
-		return uint(trailing_zeros64(u64(i)));
-	} else {
-		return uint(trailing_zeros32(u32(i)));
-	}
-}
+count_ones     :: intrinsics.count_ones;
+trailing_zeros :: intrinsics.trailing_zeros;
+reverse_bits   :: intrinsics.reverse_bits;
 
 
 leading_zeros_u8  :: proc(i:  u8) -> int {
@@ -117,10 +96,17 @@ byte_swap :: proc{
 	byte_swap_int,
 };
 
-count_zeros8   :: proc(i:   u8) ->   u8 { return   8 - count_ones8(i); }
-count_zeros16  :: proc(i:  u16) ->  u16 { return  16 - count_ones16(i); }
-count_zeros32  :: proc(i:  u32) ->  u32 { return  32 - count_ones32(i); }
-count_zeros64  :: proc(i:  u64) ->  u64 { return  64 - count_ones64(i); }
+count_zeros8   :: proc(i:   u8) ->   u8 { return   8 - count_ones(i); }
+count_zeros16  :: proc(i:  u16) ->  u16 { return  16 - count_ones(i); }
+count_zeros32  :: proc(i:  u32) ->  u32 { return  32 - count_ones(i); }
+count_zeros64  :: proc(i:  u64) ->  u64 { return  64 - count_ones(i); }
+
+count_zeros :: proc{
+	count_zeros8,
+	count_zeros16,
+	count_zeros32,
+	count_zeros64,
+};
 
 
 rotate_left8 :: proc(x: u8,  k: int) -> u8 {
@@ -176,120 +162,10 @@ to_le_u64  :: proc(i:  u64) ->  u64 { when ODIN_ENDIAN == "little" { return i; }
 to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
 
 
-@(default_calling_convention="none")
-foreign {
-	@(link_name="llvm.uadd.with.overflow.i8")  overflowing_add_u8  :: proc(lhs, rhs:  u8) -> (u8, bool)  ---
-	@(link_name="llvm.sadd.with.overflow.i8")  overflowing_add_i8  :: proc(lhs, rhs:  i8) -> (i8, bool)  ---
-	@(link_name="llvm.uadd.with.overflow.i16") overflowing_add_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
-	@(link_name="llvm.sadd.with.overflow.i16") overflowing_add_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
-	@(link_name="llvm.uadd.with.overflow.i32") overflowing_add_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
-	@(link_name="llvm.sadd.with.overflow.i32") overflowing_add_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
-	@(link_name="llvm.uadd.with.overflow.i64") overflowing_add_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
-	@(link_name="llvm.sadd.with.overflow.i64") overflowing_add_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
-}
-
-overflowing_add_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
-	when size_of(uint) == size_of(u32) {
-		x, ok := overflowing_add_u32(u32(lhs), u32(rhs));
-		return uint(x), ok;
-	} else {
-		x, ok := overflowing_add_u64(u64(lhs), u64(rhs));
-		return uint(x), ok;
-	}
-}
-overflowing_add_int :: proc(lhs, rhs: int) -> (int, bool) {
-	when size_of(int) == size_of(i32) {
-		x, ok := overflowing_add_i32(i32(lhs), i32(rhs));
-		return int(x), ok;
-	} else {
-		x, ok := overflowing_add_i64(i64(lhs), i64(rhs));
-		return int(x), ok;
-	}
-}
-
-overflowing_add :: proc{
-	overflowing_add_u8,   overflowing_add_i8,
-	overflowing_add_u16,  overflowing_add_i16,
-	overflowing_add_u32,  overflowing_add_i32,
-	overflowing_add_u64,  overflowing_add_i64,
-	overflowing_add_uint, overflowing_add_int,
-};
 
-@(default_calling_convention="none")
-foreign {
-	@(link_name="llvm.usub.with.overflow.i8")  overflowing_sub_u8  :: proc(lhs, rhs:  u8) -> (u8, bool)  ---
-	@(link_name="llvm.ssub.with.overflow.i8")  overflowing_sub_i8  :: proc(lhs, rhs:  i8) -> (i8, bool)  ---
-	@(link_name="llvm.usub.with.overflow.i16") overflowing_sub_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
-	@(link_name="llvm.ssub.with.overflow.i16") overflowing_sub_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
-	@(link_name="llvm.usub.with.overflow.i32") overflowing_sub_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
-	@(link_name="llvm.ssub.with.overflow.i32") overflowing_sub_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
-	@(link_name="llvm.usub.with.overflow.i64") overflowing_sub_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
-	@(link_name="llvm.ssub.with.overflow.i64") overflowing_sub_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
-}
-overflowing_sub_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
-	when size_of(uint) == size_of(u32) {
-		x, ok := overflowing_sub_u32(u32(lhs), u32(rhs));
-		return uint(x), ok;
-	} else {
-		x, ok := overflowing_sub_u64(u64(lhs), u64(rhs));
-		return uint(x), ok;
-	}
-}
-overflowing_sub_int :: proc(lhs, rhs: int) -> (int, bool) {
-	when size_of(int) == size_of(i32) {
-		x, ok := overflowing_sub_i32(i32(lhs), i32(rhs));
-		return int(x), ok;
-	} else {
-		x, ok := overflowing_sub_i64(i64(lhs), i64(rhs));
-		return int(x), ok;
-	}
-}
-
-overflowing_sub :: proc{
-	overflowing_sub_u8,   overflowing_sub_i8,
-	overflowing_sub_u16,  overflowing_sub_i16,
-	overflowing_sub_u32,  overflowing_sub_i32,
-	overflowing_sub_u64,  overflowing_sub_i64,
-	overflowing_sub_uint, overflowing_sub_int,
-};
-
-@(default_calling_convention="none")
-foreign {
-	@(link_name="llvm.umul.with.overflow.i8")  overflowing_mul_u8  :: proc(lhs, rhs:  u8) -> (u8, bool)  ---
-	@(link_name="llvm.smul.with.overflow.i8")  overflowing_mul_i8  :: proc(lhs, rhs:  i8) -> (i8, bool)  ---
-	@(link_name="llvm.umul.with.overflow.i16") overflowing_mul_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
-	@(link_name="llvm.smul.with.overflow.i16") overflowing_mul_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
-	@(link_name="llvm.umul.with.overflow.i32") overflowing_mul_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
-	@(link_name="llvm.smul.with.overflow.i32") overflowing_mul_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
-	@(link_name="llvm.umul.with.overflow.i64") overflowing_mul_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
-	@(link_name="llvm.smul.with.overflow.i64") overflowing_mul_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
-}
-overflowing_mul_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
-	when size_of(uint) == size_of(u32) {
-		x, ok := overflowing_mul_u32(u32(lhs), u32(rhs));
-		return uint(x), ok;
-	} else {
-		x, ok := overflowing_mul_u64(u64(lhs), u64(rhs));
-		return uint(x), ok;
-	}
-}
-overflowing_mul_int :: proc(lhs, rhs: int) -> (int, bool) {
-	when size_of(int) == size_of(i32) {
-		x, ok := overflowing_mul_i32(i32(lhs), i32(rhs));
-		return int(x), ok;
-	} else {
-		x, ok := overflowing_mul_i64(i64(lhs), i64(rhs));
-		return int(x), ok;
-	}
-}
-
-overflowing_mul :: proc{
-	overflowing_mul_u8,   overflowing_mul_i8,
-	overflowing_mul_u16,  overflowing_mul_i16,
-	overflowing_mul_u32,  overflowing_mul_i32,
-	overflowing_mul_u64,  overflowing_mul_i64,
-	overflowing_mul_uint, overflowing_mul_int,
-};
+overflowing_add :: intrinsics.overflow_add;
+overflowing_sub :: intrinsics.overflow_sub;
+overflowing_mul :: intrinsics.overflow_mul;
 
 
 len_u8 :: proc(x: u8) -> int {

+ 5 - 12
core/runtime/core.odin

@@ -20,6 +20,8 @@
 //
 package runtime
 
+import "intrinsics"
+
 // NOTE(bill): This must match the compiler's
 Calling_Convention :: enum u8 {
 	Invalid     = 0,
@@ -430,17 +432,9 @@ typeid_base_without_enum :: typeid_core;
 
 
 
-@(default_calling_convention = "none")
-foreign {
-	@(link_name="llvm.debugtrap")
-	debug_trap :: proc() ---;
-
-	@(link_name="llvm.trap")
-	trap :: proc() -> ! ---;
-
-	@(link_name="llvm.readcyclecounter")
-	read_cycle_counter :: proc() -> u64 ---;
-}
+debug_trap         :: intrinsics.debug_trap;
+trap               :: intrinsics.trap;
+read_cycle_counter :: intrinsics.read_cycle_counter;
 
 
 
@@ -488,7 +482,6 @@ __init_context :: proc "contextless" (c: ^Context) {
 	c.logger.data = nil;
 }
 
-
 default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) {
 	print_caller_location(loc);
 	print_string(" ");

+ 7 - 10
core/runtime/core_builtin.odin

@@ -1,5 +1,7 @@
 package runtime
 
+import "intrinsics"
+
 @builtin
 Maybe :: union(T: typeid) #maybe {T};
 
@@ -539,20 +541,15 @@ excl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) {
 @builtin
 card :: proc(s: $S/bit_set[$E; $U]) -> int {
 	when size_of(S) == 1 {
-		foreign { @(link_name="llvm.ctpop.i8")  count_ones :: proc(i: u8) -> u8 --- }
-		return int(count_ones(transmute(u8)s));
+		return int(intrinsics.count_ones(transmute(u8)s));
 	} else when size_of(S) == 2 {
-		foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- }
-		return int(count_ones(transmute(u16)s));
+		return int(intrinsics.count_ones(transmute(u16)s));
 	} else when size_of(S) == 4 {
-		foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- }
-		return int(count_ones(transmute(u32)s));
+		return int(intrinsics.count_ones(transmute(u32)s));
 	} else when size_of(S) == 8 {
-		foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- }
-		return int(count_ones(transmute(u64)s));
+		return int(intrinsics.count_ones(transmute(u64)s));
 	} else when size_of(S) == 16 {
-		foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- }
-		return int(count_ones(transmute(u128)s));
+		return int(intrinsics.count_ones(transmute(u128)s));
 	} else {
 		#panic("Unhandled card bit_set size");
 	}

+ 9 - 36
core/runtime/internal.odin

@@ -415,59 +415,32 @@ foreign {
 	@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
 }
 abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
-	foreign {
-		@(link_name="llvm.fabs.f16") _abs :: proc "none" (x: f16) -> f16 ---
-	}
-	return _abs(x);
+	return -x if x < 0 else x;
 }
 abs_f32 :: #force_inline proc "contextless" (x: f32) -> f32 {
-	foreign {
-		@(link_name="llvm.fabs.f32") _abs :: proc "none" (x: f32) -> f32 ---
-	}
-	return _abs(x);
+	return -x if x < 0 else x;
 }
 abs_f64 :: #force_inline proc "contextless" (x: f64) -> f64 {
-	foreign {
-		@(link_name="llvm.fabs.f64") _abs :: proc "none" (x: f64) -> f64 ---
-	}
-	return _abs(x);
+	return -x if x < 0 else x;
 }
 
 min_f16 :: proc(a, b: f16) -> f16 {
-	foreign {
-		@(link_name="llvm.minnum.f16") _min :: proc "none" (a, b: f16) -> f16 ---
-	}
-	return _min(a, b);
+	return a if a < b else b;
 }
 min_f32 :: proc(a, b: f32) -> f32 {
-	foreign {
-		@(link_name="llvm.minnum.f32") _min :: proc "none" (a, b: f32) -> f32 ---
-	}
-	return _min(a, b);
+	return a if a < b else b;
 }
 min_f64 :: proc(a, b: f64) -> f64 {
-	foreign {
-		@(link_name="llvm.minnum.f64") _min :: proc "none" (a, b: f64) -> f64 ---
-	}
-	return _min(a, b);
+	return a if a < b else b;
 }
 max_f16 :: proc(a, b: f16) -> f16 {
-	foreign {
-		@(link_name="llvm.maxnum.f16") _max :: proc "none" (a, b: f16) -> f16 ---
-	}
-	return _max(a, b);
+	return a if a > b else b;
 }
 max_f32 :: proc(a, b: f32) -> f32 {
-	foreign {
-		@(link_name="llvm.maxnum.f32") _max :: proc "none" (a, b: f32) -> f32 ---
-	}
-	return _max(a, b);
+	return a if a > b else b;
 }
 max_f64 :: proc(a, b: f64) -> f64 {
-	foreign {
-		@(link_name="llvm.maxnum.f64") _max :: proc "none" (a, b: f64) -> f64 ---
-	}
-	return _max(a, b);
+	return a if a > b else b;
 }
 
 abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {

+ 3 - 7
core/runtime/internal_linux.odin

@@ -1,5 +1,7 @@
 package runtime
 
+import "intrinsics"
+
 @(link_name="__umodti3")
 umodti3 :: proc "c" (a, b: u128) -> u128 {
 	r: u128 = ---;
@@ -86,12 +88,6 @@ fixdfti :: proc(a: u64) -> i128 {
 
 }
 
-@(default_calling_convention = "none")
-foreign {
-	@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
-}
-
-
 @(link_name="__floattidf")
 floattidf :: proc(a: i128) -> f64 {
 	DBL_MANT_DIG :: 53;
@@ -102,7 +98,7 @@ floattidf :: proc(a: i128) -> f64 {
 	N :: size_of(i128) * 8;
 	s := a >> (N-1);
 	a = (a ~ s) - s;
-	sd: = N - _clz_i128(a);  // number of significant digits
+	sd: = N - intrinsics.leading_zeros(a);  // number of significant digits
 	e := u32(sd - 1);        // exponent
 	if sd > DBL_MANT_DIG {
 		switch sd {

+ 3 - 7
core/runtime/internal_windows.odin

@@ -1,5 +1,7 @@
 package runtime
 
+import "intrinsics"
+
 @(link_name="__umodti3")
 umodti3 :: proc "c" (a, b: u128) -> u128 {
 	r: u128 = ---;
@@ -86,12 +88,6 @@ fixdfti :: proc(a: u64) -> i128 {
 
 }
 
-@(default_calling_convention = "none")
-foreign {
-	@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
-}
-
-
 @(link_name="__floattidf")
 floattidf :: proc(a: i128) -> f64 {
 	DBL_MANT_DIG :: 53;
@@ -102,7 +98,7 @@ floattidf :: proc(a: i128) -> f64 {
 	N :: size_of(i128) * 8;
 	s := a >> (N-1);
 	a = (a ~ s) - s;
-	sd: = N - _clz_i128(a);  // number of significant digits
+	sd: = N - intrinsics.leading_zeros((a);  // number of significant digits
 	e := u32(sd - 1);        // exponent
 	if sd > DBL_MANT_DIG {
 		switch sd {

+ 4 - 28
core/runtime/udivmod128.odin

@@ -1,35 +1,11 @@
 package runtime
 
-@(default_calling_convention="none")
-foreign {
-	@(link_name="llvm.cttz.i8")  _ctz_u8  :: proc(i:  u8,  is_zero_undef := false) ->  u8 ---
-	@(link_name="llvm.cttz.i16") _ctz_u16 :: proc(i: u16,  is_zero_undef := false) -> u16 ---
-	@(link_name="llvm.cttz.i32") _ctz_u32 :: proc(i: u32,  is_zero_undef := false) -> u32 ---
-	@(link_name="llvm.cttz.i64") _ctz_u64 :: proc(i: u64,  is_zero_undef := false) -> u64 ---
-}
-_ctz :: proc{
-	_ctz_u8,
-	_ctz_u16,
-	_ctz_u32,
-	_ctz_u64,
-};
-
-@(default_calling_convention="none")
-foreign {
-	@(link_name="llvm.ctlz.i8")  _clz_u8  :: proc(i:  u8,  is_zero_undef := false) ->  u8 ---
-	@(link_name="llvm.ctlz.i16") _clz_u16 :: proc(i: u16,  is_zero_undef := false) -> u16 ---
-	@(link_name="llvm.ctlz.i32") _clz_u32 :: proc(i: u32,  is_zero_undef := false) -> u32 ---
-	@(link_name="llvm.ctlz.i64") _clz_u64 :: proc(i: u64,  is_zero_undef := false) -> u64 ---
-}
-_clz :: proc{
-	_clz_u8,
-	_clz_u16,
-	_clz_u32,
-	_clz_u64,
-};
-
+import "intrinsics"
 
 udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
+	_ctz :: intrinsics.trailing_zeros;
+	_clz :: intrinsics.leading_zeros;
+
 	n := transmute([2]u64)a;
 	d := transmute([2]u64)b;
 	q, r: [2]u64 = ---, ---;

+ 3 - 5
core/time/time.odin

@@ -1,5 +1,7 @@
 package time
 
+import "intrinsics"
+
 Duration :: distinct i64;
 
 Nanosecond  :: Duration(1);
@@ -137,11 +139,7 @@ clock :: proc(t: Time) -> (hour, min, sec: int) {
 
 
 read_cycle_counter :: proc() -> u64 {
-	foreign _ {
-		@(link_name="llvm.readcyclecounter")
-		llvm_readcyclecounter :: proc "none" () -> u64 ---
-	}
-	return llvm_readcyclecounter();
+	return u64(intrinsics.read_cycle_counter());
 }
 
 

+ 1 - 0
src/check_builtin.cpp

@@ -1924,6 +1924,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 	case BuiltinProc_count_ones:
 	case BuiltinProc_trailing_zeros:
+	case BuiltinProc_leading_zeros:
 	case BuiltinProc_reverse_bits:
 		{
 			Operand x = {};

+ 2 - 0
src/checker_builtin_procs.hpp

@@ -47,6 +47,7 @@ enum BuiltinProcId {
 
 	BuiltinProc_count_ones,
 	BuiltinProc_trailing_zeros,
+	BuiltinProc_leading_zeros,
 	BuiltinProc_reverse_bits,
 	BuiltinProc_byte_swap,
 
@@ -265,6 +266,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 
 	{STR_LIT("count_ones"),     1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("trailing_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("leading_zeros"),  1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("reverse_bits"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("byte_swap"),      1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 

+ 22 - 0
src/llvm_backend.cpp

@@ -9108,6 +9108,8 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 
 	case BuiltinProc_trailing_zeros:
 		return lb_emit_trailing_zeros(p, lb_build_expr(p, ce->args[0]), tv.type);
+	case BuiltinProc_leading_zeros:
+		return lb_emit_leading_zeros(p, lb_build_expr(p, ce->args[0]), tv.type);
 
 	case BuiltinProc_count_ones:
 		return lb_emit_count_ones(p, lb_build_expr(p, ce->args[0]), tv.type);
@@ -9989,6 +9991,26 @@ lbValue lb_emit_trailing_zeros(lbProcedure *p, lbValue x, Type *type) {
 	return res;
 }
 
+lbValue lb_emit_leading_zeros(lbProcedure *p, lbValue x, Type *type) {
+	x = lb_emit_conv(p, x, type);
+
+	char const *name = "llvm.ctlz";
+	LLVMTypeRef types[1] = {lb_type(p->module, type)};
+	unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+	GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+	LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+	LLVMValueRef args[2] = {};
+	args[0] = x.value;
+	args[1] = LLVMConstNull(LLVMInt1TypeInContext(p->module->ctx));
+
+	lbValue res = {};
+	res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+	res.type = type;
+	return res;
+}
+
+
 
 lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type) {
 	x = lb_emit_conv(p, x, type);

+ 1 - 0
src/llvm_backend.hpp

@@ -396,6 +396,7 @@ LLVMMetadataRef lb_debug_type(lbModule *m, Type *type);
 
 lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type);
 lbValue lb_emit_trailing_zeros(lbProcedure *p, lbValue x, Type *type);
+lbValue lb_emit_leading_zeros(lbProcedure *p, lbValue x, Type *type);
 lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type);
 
 lbValue lb_emit_bit_set_card(lbProcedure *p, lbValue x);