aboutsummaryrefslogtreecommitdiff
path: root/src/link/riscv.zig
blob: cfc42ac33969e1596982513fbad2216a4c52b62f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
pub fn writeSetSub6(comptime op: enum { set, sub }, code: *[1]u8, addend: anytype) void {
    const mask: u8 = 0b11_000000;
    const actual: i8 = @truncate(addend);
    var value: u8 = mem.readInt(u8, code, .little);
    switch (op) {
        .set => value = (value & mask) | @as(u8, @bitCast(actual & ~mask)),
        .sub => value = (value & mask) | (@as(u8, @bitCast(@as(i8, @bitCast(value)) -| actual)) & ~mask),
    }
    mem.writeInt(u8, code, value, .little);
}

pub fn writeSubUleb(code: []u8, addend: i64) void {
    var reader: std.Io.Reader = .fixed(code);
    const value = reader.takeLeb128(u64) catch unreachable;
    overwriteUleb(code, value -% @as(u64, @intCast(addend)));
}

pub fn writeSetUleb(code: []u8, addend: i64) void {
    overwriteUleb(code, @intCast(addend));
}

fn overwriteUleb(code: []u8, addend: u64) void {
    var value: u64 = addend;
    var i: usize = 0;

    while (true) {
        const byte = code[i];
        if (byte & 0x80 == 0) break;
        code[i] = 0x80 | @as(u8, @truncate(value & 0x7f));
        i += 1;
        value >>= 7;
    }
    code[i] = @truncate(value & 0x7f);
}

pub fn writeAddend(
    comptime Int: type,
    comptime op: enum { add, sub },
    code: *[@typeInfo(Int).int.bits / 8]u8,
    value: anytype,
) void {
    var V: Int = mem.readInt(Int, code, .little);
    const addend: Int = @truncate(value);
    switch (op) {
        .add => V +|= addend, // TODO: I think saturating arithmetic is correct here
        .sub => V -|= addend,
    }
    mem.writeInt(Int, code, V, .little);
}

pub fn writeInstU(code: *[4]u8, value: u32) void {
    var data: Instruction = .{ .U = mem.bytesToValue(@FieldType(Instruction, "U"), code) };
    const compensated: u32 = @bitCast(@as(i32, @bitCast(value)) + 0x800);
    data.U.imm12_31 = bitSlice(compensated, 31, 12);
    mem.writeInt(u32, code, data.toU32(), .little);
}

pub fn writeInstI(code: *[4]u8, value: u32) void {
    var data: Instruction = .{ .I = mem.bytesToValue(@FieldType(Instruction, "I"), code) };
    data.I.imm0_11 = bitSlice(value, 11, 0);
    mem.writeInt(u32, code, data.toU32(), .little);
}

pub fn writeInstS(code: *[4]u8, value: u32) void {
    var data: Instruction = .{ .S = mem.bytesToValue(@FieldType(Instruction, "S"), code) };
    data.S.imm0_4 = bitSlice(value, 4, 0);
    data.S.imm5_11 = bitSlice(value, 11, 5);
    mem.writeInt(u32, code, data.toU32(), .little);
}

pub fn writeInstJ(code: *[4]u8, value: u32) void {
    var data: Instruction = .{ .J = mem.bytesToValue(@FieldType(Instruction, "J"), code) };
    data.J.imm1_10 = bitSlice(value, 10, 1);
    data.J.imm11 = bitSlice(value, 11, 11);
    data.J.imm12_19 = bitSlice(value, 19, 12);
    data.J.imm20 = bitSlice(value, 20, 20);
    mem.writeInt(u32, code, data.toU32(), .little);
}

pub fn writeInstB(code: *[4]u8, value: u32) void {
    var data: Instruction = .{ .B = mem.bytesToValue(@FieldType(Instruction, "B"), code) };
    data.B.imm1_4 = bitSlice(value, 4, 1);
    data.B.imm5_10 = bitSlice(value, 10, 5);
    data.B.imm11 = bitSlice(value, 11, 11);
    data.B.imm12 = bitSlice(value, 12, 12);
    mem.writeInt(u32, code, data.toU32(), .little);
}

fn bitSlice(
    value: anytype,
    comptime high: comptime_int,
    comptime low: comptime_int,
) std.math.IntFittingRange(0, 1 << high - low) {
    return @truncate((value >> low) & (1 << (high - low + 1)) - 1);
}

pub const Eflags = packed struct(u32) {
    rvc: bool,
    fabi: FloatAbi,
    rve: bool,
    tso: bool,
    _reserved: u19 = 0,
    _unused: u8 = 0,

    pub const FloatAbi = enum(u2) {
        soft = 0b00,
        single = 0b01,
        double = 0b10,
        quad = 0b11,
    };
};

const mem = std.mem;
const std = @import("std");

const encoding = @import("../codegen/riscv64/encoding.zig");
const Instruction = encoding.Instruction;