Implement leading and trailing zeroes

This commit is contained in:
David Gonzalez Martin 2024-06-06 21:51:42 -06:00
parent b549b6ed49
commit 01f3e5beae
4 changed files with 149 additions and 4 deletions

View File

@ -1,6 +1,3 @@
size
trailing zeroes
leading zeroes
returns inside loops (if conditional)
returns inside loops (else conditional)
returns inside loops (non-conditional)

View File

@ -242,6 +242,52 @@ const Parser = struct{
});
return &constant_int.value;
},
.trailing_zeroes => {
parser.skip_space(src);
parser.expect_character(src, '(');
parser.skip_space(src);
const value = parser.parse_expression(analyzer, thread, file, ty, .right);
parser.skip_space(src);
parser.expect_character(src, ')');
const tz = thread.trailing_zeroes.append(.{
.value = value,
.instruction = .{
.id = .trailing_zeroes,
.value = .{
.sema = .{
.id = .instruction,
.thread = thread.get_index(),
.resolved = true,
},
},
},
});
_ = analyzer.current_basic_block.instructions.append(&tz.instruction);
return &tz.instruction.value;
},
.leading_zeroes => {
parser.skip_space(src);
parser.expect_character(src, '(');
parser.skip_space(src);
const value = parser.parse_expression(analyzer, thread, file, ty, .right);
parser.skip_space(src);
parser.expect_character(src, ')');
const lz = thread.leading_zeroes.append(.{
.value = value,
.instruction = .{
.id = .leading_zeroes,
.value = .{
.sema = .{
.id = .instruction,
.thread = thread.get_index(),
.resolved = true,
},
},
},
});
_ = analyzer.current_basic_block.instructions.append(&lz.instruction);
return &lz.instruction.value;
},
else => |t| @panic(@tagName(t)),
}
}
@ -1518,6 +1564,14 @@ const Value = struct {
.integer_compare => {
return &instance.threads[value.sema.thread].integers[0].type;
},
.trailing_zeroes => {
const tz = instruction.get_payload(.trailing_zeroes);
return tz.value.get_type();
},
.leading_zeroes => {
const lz = instruction.get_payload(.leading_zeroes);
return lz.value.get_type();
},
else => |t| @panic(@tagName(t)),
};
},
@ -1582,7 +1636,9 @@ const Keyword = enum{
const Intrinsic = enum{
assert,
leading_zeroes,
size,
trailing_zeroes,
trap,
@"unreachable",
};
@ -1802,12 +1858,14 @@ const Instruction = struct{
integer_binary_operation,
integer_compare,
jump,
leading_zeroes,
load,
local_symbol,
phi,
ret,
ret_void,
store,
trailing_zeroes,
@"unreachable",
};
@ -1818,12 +1876,14 @@ const Instruction = struct{
.integer_binary_operation = IntegerBinaryOperation,
.integer_compare = IntegerCompare,
.jump = Jump,
.leading_zeroes = LeadingZeroes,
.local_symbol = LocalSymbol,
.load = Load,
.phi = Phi,
.ret = Return,
.ret_void = void,
.store = Store,
.trailing_zeroes = TrailingZeroes,
.@"unreachable" = Unreachable,
});
@ -1950,6 +2010,16 @@ fn get_power_of_two_byte_count_from_bit_count(bit_count: u32) u32 {
unreachable;
}
const LeadingZeroes = struct{
instruction: Instruction,
value: *Value,
};
const TrailingZeroes = struct{
instruction: Instruction,
value: *Value,
};
const Thread = struct{
arena: *Arena = undefined,
functions: PinnedArray(Function) = .{},
@ -1976,6 +2046,8 @@ const Thread = struct{
argument_symbols: PinnedArray(ArgumentSymbol) = .{},
global_variables: PinnedArray(GlobalVariable) = .{},
unreachables: PinnedArray(Unreachable) = .{},
leading_zeroes: PinnedArray(LeadingZeroes) = .{},
trailing_zeroes: PinnedArray(TrailingZeroes) = .{},
analyzed_file_count: u32 = 0,
assigned_file_count: u32 = 0,
llvm: struct {
@ -1984,6 +2056,9 @@ const Thread = struct{
attributes: LLVM.Attributes,
target_machine: *LLVM.Target.Machine,
object: ?[]const u8 = null,
intrinsic_ids: std.EnumArray(LLVMIntrinsic, LLVM.Value.IntrinsicID),
intrinsic_id_map: PinnedHashMap([]const u8, LLVM.Value.IntrinsicID) = .{},
intrinsic_function_map: PinnedHashMap(LLVMIntrinsic.Parameters, *LLVM.Value.Constant.Function) = .{},
} = undefined,
integers: [128]Type.Integer = blk: {
var integers: [128]Type.Integer = undefined;
@ -2054,6 +2129,16 @@ const Thread = struct{
}
};
const LLVMIntrinsic = enum{
leading_zeroes,
trailing_zeroes,
const Parameters = struct{
id: LLVM.Value.IntrinsicID,
types: []const *LLVM.Type,
};
};
const LLVMFile = struct {
file: *LLVM.DebugInfo.File,
compile_unit: *LLVM.DebugInfo.CompileUnit,
@ -3117,10 +3202,13 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void {
.module = module,
.attributes = attributes,
.target_machine = target_machine,
.intrinsic_ids = @TypeOf(thread.llvm.intrinsic_ids).init(.{
.leading_zeroes = llvm_get_intrinsic_id("llvm.ctlz"),
.trailing_zeroes = llvm_get_intrinsic_id("llvm.cttz"),
}),
};
const debug_info = false;
for (thread.external_functions.slice()) |*nat_function| {
_ = llvm_get_function(thread, nat_function, true);
}
@ -3329,6 +3417,38 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void {
const ur = builder.createUnreachable();
break :block ur.toValue();
},
.leading_zeroes => block: {
const leading_zeroes = instruction.get_payload(.leading_zeroes);
const v = llvm_get_value(thread, leading_zeroes.value);
const v_type = v.getType();
const lz_id = thread.llvm.intrinsic_ids.get(.leading_zeroes);
const parameters = LLVMIntrinsic.Parameters{
.id = lz_id,
.types = &.{v_type},
};
const intrinsic_function = llvm_get_intrinsic_function(thread, parameters);
const intrinsic_function_type = intrinsic_function.getType();
const is_poison = context.getConstantInt(1, 0, false);
const args: []const *LLVM.Value = &.{v, is_poison.toValue()};
const call_i = builder.createCall(intrinsic_function_type, intrinsic_function.toValue(), args.ptr, args.len, "", "".len, null);
break :block call_i.toValue();
},
.trailing_zeroes => block: {
const trailing_zeroes = instruction.get_payload(.trailing_zeroes);
const v = llvm_get_value(thread, trailing_zeroes.value);
const v_type = v.getType();
const tz_id = thread.llvm.intrinsic_ids.get(.trailing_zeroes);
const parameters = LLVMIntrinsic.Parameters{
.id = tz_id,
.types = &.{v_type},
};
const intrinsic_function = llvm_get_intrinsic_function(thread, parameters);
const intrinsic_function_type = intrinsic_function.getType();
const is_poison = context.getConstantInt(1, 0, false);
const args: []const *LLVM.Value = &.{v, is_poison.toValue()};
const call_i = builder.createCall(intrinsic_function_type, intrinsic_function.toValue(), args.ptr, args.len, "", "".len, null);
break :block call_i.toValue();
},
else => |t| @panic(@tagName(t)),
};
@ -3434,6 +3554,20 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void {
}
}
fn llvm_get_intrinsic_id(intrinsic: []const u8) LLVM.Value.IntrinsicID{
const intrinsic_id = LLVM.lookupIntrinsic(intrinsic.ptr, intrinsic.len);
assert(intrinsic_id != .none);
return intrinsic_id;
}
fn llvm_get_intrinsic_function(thread: *Thread, parameters: LLVMIntrinsic.Parameters) *LLVM.Value.Constant.Function{
if (thread.llvm.intrinsic_function_map.get(parameters)) |llvm| return llvm else {
const intrinsic_function = thread.llvm.module.getIntrinsicDeclaration(parameters.id, parameters.types.ptr, parameters.types.len);
thread.llvm.intrinsic_function_map.put_no_clobber(parameters, intrinsic_function);
return intrinsic_function;
}
}
fn llvm_get_value(thread: *Thread, value: *Value) *LLVM.Value {
if (value.llvm) |llvm| {
assert(value.sema.thread == thread.get_index());

View File

@ -0,0 +1,7 @@
fn[cc(.c)] main[export]() s32 {
>a: s32 = 0xffffffff;
#assert(#leading_zeroes(a) == 0);
>b: s32 = 0x1fffffff;
#assert(#leading_zeroes(b) == 3);
return 0;
}

View File

@ -0,0 +1,7 @@
fn[cc(.c)] main[export]() s32 {
>a: s32 = 7;
#assert(#trailing_zeroes(a) == 0);
>b: s32 = 8;
#assert(#trailing_zeroes(b) == 3);
return 0;
}