@int_cast, @bit_cast

Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
Jakub Doka 2025-02-25 06:54:33 +01:00
parent 613c6ab4df
commit a083802e76
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
7 changed files with 115 additions and 8 deletions

View file

@ -1 +1 @@
/vendored-tests/
# /vendored-tests/

View file

@ -808,7 +808,20 @@ main := fn(): uint {
}
```
####
#### directives 4 (@int_cast)
```hb
main := fn(): u8 {
v: uint = 0
return @int_cast(v)
}
```
#### directives 5 (@bit_cast)
```hb
main := fn(): u32 {
return @bit_cast(@as(struct{.l: u16; .r: u16}, @bit_cast(@as(u32, 0))))
}
```
## progress
@ -856,7 +869,7 @@ main := fn(): uint {
- [x] structs
- [ ] indexing
- [ ] packed
- [X] constructors
- [x] constructors
- [x] dictionary
- [x] tuple
- [ ] default values
@ -882,16 +895,16 @@ main := fn(): uint {
- [x] `@use(<string>)`
- [x] `@TypeOf(<expr>)`
- [x] `@as(<ty>, <expr>)`
- [ ] `@int_cast(<expr>)`
- [x] `@int_cast(<expr>)`
- [x] `@size_of(<ty>)`
- [x] `@align_of(<ty>)`
- [ ] `@bit_cast(<expr>)`
- [ ] `@ecall(...<expr>)`
- [x] `@bit_cast(<expr>)`
- [x] `@ecall(...<expr>)`
- [ ] `@embed(<string>)`
- [ ] `@inline(<func>, ...<args>)`
- [ ] `@len_of(<ty>)`
- [ ] `@kind_of(<ty>)`
- [ ] `@Any()`
- [ ] `@Any(<fn(type): bool/type>..)`
- [ ] `@error(...<expr>)`
- [ ] `@ChildOf(<ty>)`
- [ ] `@target("<pat>")`

View file

@ -994,21 +994,100 @@ pub fn emit(self: *Codegen, ctx: Ctx, expr: Ast.Id) Value {
const name = ast.tokenSrc(e.pos.index);
const args = ast.exprs.view(e.args);
const utils = enum {
fn reportInferrence(cg: *Codegen, exr: anytype, ty: []const u8, dir_name: []const u8) void {
cg.report(exr, "type can not be inferred from the context, use `@as(<{s}>, {s}(...))`", .{ ty, dir_name });
}
fn assertArgs(cg: *Codegen, exr: anytype, got: []const Ast.Id, comptime expected: []const u8) bool {
const min_expected_args = comptime std.mem.count(u8, expected, ",") + @intFromBool(expected.len != 0);
const varargs = comptime std.mem.endsWith(u8, expected, "..");
if (got.len < min_expected_args or (!varargs and got.len > min_expected_args)) {
const range = if (varargs) "at least " else "";
cg.report(
exr,
"directive takes {s}{} arguments, got {} (" ++ expected ++ ")",
.{ range, min_expected_args, got.len },
);
return true;
}
return false;
}
};
if (eql(u8, name, "@CurrentScope")) {
if (utils.assertArgs(self, expr, args, "")) return .never;
return self.emitTyConst(self.parent_scope.perm());
} else if (eql(u8, name, "@TypeOf")) {
if (utils.assertArgs(self, expr, args, "<ty>")) return .never;
const ty = self.types.ct.jitExpr("", .{ .Tmp = self }, .{}, args[0]).?[1];
return self.emitTyConst(ty);
} else if (eql(u8, name, "@int_cast")) {
if (utils.assertArgs(self, expr, args, "<expr>")) return .never;
const ret: Types.Id = ctx.ty orelse {
utils.reportInferrence(self, expr, "int-ty", name);
return .never;
};
if (!ret.isInteger()) {
self.report(expr, "inferred type must be an integer, {} is not", .{ret});
return .never;
}
var oper = self.emit(.{}, args[0]);
if (!oper.ty.isInteger()) {
self.report(args[0], "expeced integer, {} is not", .{oper.ty});
return .never;
}
self.ensureLoaded(&oper);
return .mkv(ret, self.bl.addUnOp(.ired, self.abi.categorize(ret, self.types).ByValue, oper.id.Value));
} else if (eql(u8, name, "@bit_cast")) {
if (utils.assertArgs(self, expr, args, "<expr>")) return .never;
const ret: Types.Id = ctx.ty orelse {
utils.reportInferrence(self, expr, "ty", name);
return .never;
};
var oper = self.emit(.{}, args[0]);
if (oper.ty.size(self.types) != ret.size(self.types)) {
self.report(
args[0],
"cant bitcast from {} to {} because sizes are not equal ({} != {})",
.{ oper.ty, ret, oper.ty.size(self.types), ret.size(self.types) },
);
return .never;
}
const to_abi = self.abi.categorize(ret, self.types);
if (to_abi != .ByValue) {
const loc = self.bl.addLocal(ret.size(self.types));
self.emitGenericStore(loc, &oper);
return .mkp(ret, loc);
} else {
oper.ty = ret;
self.ensureLoaded(&oper);
return oper;
}
} else if (eql(u8, name, "@align_of")) {
if (utils.assertArgs(self, expr, args, "<ty>")) return .never;
return .mkv(.uint, self.bl.addIntImm(.int, @bitCast(self.resolveAnonTy(args[0]).alignment(self.types))));
} else if (eql(u8, name, "@size_of")) {
if (utils.assertArgs(self, expr, args, "<ty>")) return .never;
return .mkv(.uint, self.bl.addIntImm(.int, @bitCast(self.resolveAnonTy(args[0]).size(self.types))));
} else if (eql(u8, name, "@ecall")) {
if (utils.assertArgs(self, expr, args, "<expr>..")) return .never;
var tmp = root.Arena.scrath(null);
defer tmp.deinit();
const ret = ctx.ty orelse {
self.report(expr, "type can not be inferred from the context, use `@as(<ty>, @ecall(...))`", .{});
utils.reportInferrence(self, expr, "ty", name);
return .never;
};
@ -1032,6 +1111,7 @@ pub fn emit(self: *Codegen, ctx: Ctx, expr: Ast.Id) Value {
return self.assembleReturn(Comptime.eca, call_args, ctx, ret, ret_abi);
} else if (eql(u8, name, "@as")) {
if (utils.assertArgs(self, expr, args, "<ty>, <expr>")) return .never;
const ty = self.resolveAnonTy(args[0]);
return self.emitTyped(ctx, ty, args[1]);
} else std.debug.panic("unhandled directive {s}", .{name});

View file

@ -402,6 +402,10 @@ pub fn emitBlockBody(self: *HbvmGen, tmp: std.mem.Allocator, node: *Func.Node) v
const mask = (@as(u64, 1) << @intCast(inps[0].?.data_type.size() * 8)) - 1;
self.emit(.andi, .{ self.reg(no), self.reg(inps[0]), mask });
},
.ired => {
// TODO: idealize to nothing
self.emit(.cp, .{ self.reg(no), self.reg(inps[0]) });
},
.neg => {
self.emit(.neg, .{ self.reg(no), self.reg(inps[0]) });
},

View file

@ -23,6 +23,7 @@ pub const BinOp = enum(u8) {
pub const UnOp = enum(u8) {
sext,
uext,
ired,
neg,
};

View file

@ -0,0 +1,5 @@
main:
li64 $13, 0
cp $13, $13
cp $1, $13
tx

View file

@ -0,0 +1,4 @@
main:
li64 $13, 0
cp $1, $13
tx