//! Machine Intermediate Representation. //! This representation is produced by wasm Codegen. //! Each of these instructions have a 1:1 mapping to a wasm opcode, //! but may contain metadata for a specific opcode such as an immediate. //! MIR can be lowered to both textual code (wat) and binary format (wasm). //! The main benefits of MIR is optimization passes, pre-allocated locals, //! and known jump labels for blocks. const Mir = @This(); const InternPool = @import("../../InternPool.zig"); const Wasm = @import("../../link/Wasm.zig"); const Emit = @import("Emit.zig"); const Alignment = InternPool.Alignment; const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; const leb = std.leb; instructions: std.MultiArrayList(Inst).Slice, /// A slice of indexes where the meaning of the data is determined by the /// `Inst.Tag` value. extra: []const u32, locals: []const std.wasm.Valtype, prologue: Prologue, /// Not directly used by `Emit`, but the linker needs this to merge it with a global set. /// Value is the explicit alignment if greater than natural alignment, `.none` otherwise. uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), /// Not directly used by `Emit`, but the linker needs this to merge it with a global set. indirect_function_set: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), /// Not directly used by `Emit`, but the linker needs this to ensure these types are interned. func_tys: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), /// Not directly used by `Emit`, but the linker needs this to add it to its own refcount. error_name_table_ref_count: u32, pub const Prologue = extern struct { flags: Flags, sp_local: u32, stack_size: u32, bottom_stack_local: u32, pub const Flags = packed struct(u32) { stack_alignment: Alignment, padding: u26 = 0, }; pub const none: Prologue = .{ .sp_local = 0, .flags = .{ .stack_alignment = .none }, .stack_size = 0, .bottom_stack_local = 0, }; pub fn isNone(p: *const Prologue) bool { return p.flags.stack_alignment != .none; } }; pub const Inst = struct { /// The opcode that represents this instruction tag: Tag, /// Data is determined by the set `tag`. /// For example, `data` will be an i32 for when `tag` is 'i32_const'. data: Data, /// The position of a given MIR isntruction with the instruction list. pub const Index = u32; /// Some tags match wasm opcode values to facilitate trivial lowering. pub const Tag = enum(u8) { /// Uses `tag`. @"unreachable" = 0x00, /// Emits epilogue begin debug information. Marks the end of the function. /// /// Uses `tag` (no additional data). dbg_epilogue_begin, /// Creates a new block that can be jump from. /// /// Type of the block is given in data `block_type` block = 0x02, /// Creates a new loop. /// /// Type of the loop is given in data `block_type` loop = 0x03, /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the /// memory address of an unnamed constant. When emitting an object /// file, this adds a relocation. /// /// This may not refer to a function. /// /// Uses `ip_index`. uav_ref, /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the /// memory address of an unnamed constant, offset by an integer value. /// When emitting an object file, this adds a relocation. /// /// This may not refer to a function. /// /// Uses `payload` pointing to a `UavRefOff`. uav_ref_off, /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the /// memory address of a named constant. /// /// May not refer to a function. /// /// Uses `nav_index`. nav_ref, /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the /// memory address of named constant, offset by an integer value. /// When emitting an object file, this adds a relocation. /// /// May not refer to a function. /// /// Uses `payload` pointing to a `NavRefOff`. nav_ref_off, /// Lowers to an i32_const which is the index of the function in the /// table section. /// /// Uses `nav_index`. func_ref, /// Inserts debug information about the current line and column /// of the source code /// /// Uses `payload` of which the payload type is `DbgLineColumn` dbg_line, /// Lowers to an i32_const containing the number of unique Zig error /// names. /// Uses `tag`. errors_len, /// Represents the end of a function body or an initialization expression /// /// Uses `tag` (no additional data). end = 0x0B, /// Breaks from the current block to a label /// /// Uses `label` where index represents the label to jump to br = 0x0C, /// Breaks from the current block if the stack value is non-zero /// /// Uses `label` where index represents the label to jump to br_if = 0x0D, /// Jump table that takes the stack value as an index where each value /// represents the label to jump to. /// /// Data is extra of which the Payload's type is `JumpTable` br_table, /// Returns from the function /// /// Uses `tag`. @"return" = 0x0F, /// Lowers to an i32_const (wasm32) or i64_const (wasm64) containing /// the base address of the table of error code names, with each /// element being a null-terminated slice. /// /// Uses `tag`. error_name_table_ref, /// Calls a function using `nav_index`. call_nav, /// Calls a function pointer by its function signature /// and index into the function table. /// /// Uses `ip_index`; the `InternPool.Index` is the function type. call_indirect, /// Calls a function by its index. /// /// The function is the auto-generated tag name function for the type /// provided in `ip_index`. call_tag_name, /// Lowers to a `call` instruction, using `intrinsic`. call_intrinsic, /// Pops three values from the stack and pushes /// the first or second value dependent on the third value. /// Uses `tag` select = 0x1B, /// Loads a local at given index onto the stack. /// /// Uses `label` local_get = 0x20, /// Pops a value from the stack into the local at given index. /// Stack value must be of the same type as the local. /// /// Uses `label` local_set = 0x21, /// Sets a local at given index using the value at the top of the stack without popping the value. /// Stack value must have the same type as the local. /// /// Uses `label` local_tee = 0x22, /// Pops a value from the stack and sets the stack pointer global. /// The value must be the same type as the stack pointer global. /// /// Uses `tag` (no additional data). global_set_sp, /// Loads a 32-bit integer from memory (data section) onto the stack /// Pops the value from the stack which represents the offset into memory. /// /// Uses `payload` of type `MemArg`. i32_load = 0x28, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i64_load = 0x29, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` f32_load = 0x2A, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` f64_load = 0x2B, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i32_load8_s = 0x2C, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i32_load8_u = 0x2D, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i32_load16_s = 0x2E, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i32_load16_u = 0x2F, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i64_load8_s = 0x30, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i64_load8_u = 0x31, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i64_load16_s = 0x32, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i64_load16_u = 0x33, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i64_load32_s = 0x34, /// Loads a value from memory onto the stack, based on the signedness /// and bitsize of the type. /// /// Uses `payload` with type `MemArg` i64_load32_u = 0x35, /// Pops 2 values from the stack, where the first value represents the value to write into memory /// and the second value represents the offset into memory where the value must be written to. /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. /// /// Uses `payload` of type `MemArg`. i32_store = 0x36, /// Pops 2 values from the stack, where the first value represents the value to write into memory /// and the second value represents the offset into memory where the value must be written to. /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. /// /// Uses `Payload` with type `MemArg` i64_store = 0x37, /// Pops 2 values from the stack, where the first value represents the value to write into memory /// and the second value represents the offset into memory where the value must be written to. /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. /// /// Uses `Payload` with type `MemArg` f32_store = 0x38, /// Pops 2 values from the stack, where the first value represents the value to write into memory /// and the second value represents the offset into memory where the value must be written to. /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. /// /// Uses `Payload` with type `MemArg` f64_store = 0x39, /// Pops 2 values from the stack, where the first value represents the value to write into memory /// and the second value represents the offset into memory where the value must be written to. /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. /// /// Uses `Payload` with type `MemArg` i32_store8 = 0x3A, /// Pops 2 values from the stack, where the first value represents the value to write into memory /// and the second value represents the offset into memory where the value must be written to. /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. /// /// Uses `Payload` with type `MemArg` i32_store16 = 0x3B, /// Pops 2 values from the stack, where the first value represents the value to write into memory /// and the second value represents the offset into memory where the value must be written to. /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. /// /// Uses `Payload` with type `MemArg` i64_store8 = 0x3C, /// Pops 2 values from the stack, where the first value represents the value to write into memory /// and the second value represents the offset into memory where the value must be written to. /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. /// /// Uses `Payload` with type `MemArg` i64_store16 = 0x3D, /// Pops 2 values from the stack, where the first value represents the value to write into memory /// and the second value represents the offset into memory where the value must be written to. /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. /// /// Uses `Payload` with type `MemArg` i64_store32 = 0x3E, /// Returns the memory size in amount of pages. /// /// Uses `label` memory_size = 0x3F, /// Increases the memory by given number of pages. /// /// Uses `label` memory_grow = 0x40, /// Loads a 32-bit signed immediate value onto the stack /// /// Uses `imm32` i32_const, /// Loads a i64-bit signed immediate value onto the stack /// /// uses `payload` of type `Imm64` i64_const, /// Loads a 32-bit float value onto the stack. /// /// Uses `float32` f32_const, /// Loads a 64-bit float value onto the stack. /// /// Uses `payload` of type `Float64` f64_const, /// Uses `tag` i32_eqz = 0x45, /// Uses `tag` i32_eq = 0x46, /// Uses `tag` i32_ne = 0x47, /// Uses `tag` i32_lt_s = 0x48, /// Uses `tag` i32_lt_u = 0x49, /// Uses `tag` i32_gt_s = 0x4A, /// Uses `tag` i32_gt_u = 0x4B, /// Uses `tag` i32_le_s = 0x4C, /// Uses `tag` i32_le_u = 0x4D, /// Uses `tag` i32_ge_s = 0x4E, /// Uses `tag` i32_ge_u = 0x4F, /// Uses `tag` i64_eqz = 0x50, /// Uses `tag` i64_eq = 0x51, /// Uses `tag` i64_ne = 0x52, /// Uses `tag` i64_lt_s = 0x53, /// Uses `tag` i64_lt_u = 0x54, /// Uses `tag` i64_gt_s = 0x55, /// Uses `tag` i64_gt_u = 0x56, /// Uses `tag` i64_le_s = 0x57, /// Uses `tag` i64_le_u = 0x58, /// Uses `tag` i64_ge_s = 0x59, /// Uses `tag` i64_ge_u = 0x5A, /// Uses `tag` f32_eq = 0x5B, /// Uses `tag` f32_ne = 0x5C, /// Uses `tag` f32_lt = 0x5D, /// Uses `tag` f32_gt = 0x5E, /// Uses `tag` f32_le = 0x5F, /// Uses `tag` f32_ge = 0x60, /// Uses `tag` f64_eq = 0x61, /// Uses `tag` f64_ne = 0x62, /// Uses `tag` f64_lt = 0x63, /// Uses `tag` f64_gt = 0x64, /// Uses `tag` f64_le = 0x65, /// Uses `tag` f64_ge = 0x66, /// Uses `tag` i32_clz = 0x67, /// Uses `tag` i32_ctz = 0x68, /// Uses `tag` i32_popcnt = 0x69, /// Uses `tag` i32_add = 0x6A, /// Uses `tag` i32_sub = 0x6B, /// Uses `tag` i32_mul = 0x6C, /// Uses `tag` i32_div_s = 0x6D, /// Uses `tag` i32_div_u = 0x6E, /// Uses `tag` i32_rem_s = 0x6F, /// Uses `tag` i32_rem_u = 0x70, /// Uses `tag` i32_and = 0x71, /// Uses `tag` i32_or = 0x72, /// Uses `tag` i32_xor = 0x73, /// Uses `tag` i32_shl = 0x74, /// Uses `tag` i32_shr_s = 0x75, /// Uses `tag` i32_shr_u = 0x76, /// Uses `tag` i64_clz = 0x79, /// Uses `tag` i64_ctz = 0x7A, /// Uses `tag` i64_popcnt = 0x7B, /// Uses `tag` i64_add = 0x7C, /// Uses `tag` i64_sub = 0x7D, /// Uses `tag` i64_mul = 0x7E, /// Uses `tag` i64_div_s = 0x7F, /// Uses `tag` i64_div_u = 0x80, /// Uses `tag` i64_rem_s = 0x81, /// Uses `tag` i64_rem_u = 0x82, /// Uses `tag` i64_and = 0x83, /// Uses `tag` i64_or = 0x84, /// Uses `tag` i64_xor = 0x85, /// Uses `tag` i64_shl = 0x86, /// Uses `tag` i64_shr_s = 0x87, /// Uses `tag` i64_shr_u = 0x88, /// Uses `tag` f32_abs = 0x8B, /// Uses `tag` f32_neg = 0x8C, /// Uses `tag` f32_ceil = 0x8D, /// Uses `tag` f32_floor = 0x8E, /// Uses `tag` f32_trunc = 0x8F, /// Uses `tag` f32_nearest = 0x90, /// Uses `tag` f32_sqrt = 0x91, /// Uses `tag` f32_add = 0x92, /// Uses `tag` f32_sub = 0x93, /// Uses `tag` f32_mul = 0x94, /// Uses `tag` f32_div = 0x95, /// Uses `tag` f32_min = 0x96, /// Uses `tag` f32_max = 0x97, /// Uses `tag` f32_copysign = 0x98, /// Uses `tag` f64_abs = 0x99, /// Uses `tag` f64_neg = 0x9A, /// Uses `tag` f64_ceil = 0x9B, /// Uses `tag` f64_floor = 0x9C, /// Uses `tag` f64_trunc = 0x9D, /// Uses `tag` f64_nearest = 0x9E, /// Uses `tag` f64_sqrt = 0x9F, /// Uses `tag` f64_add = 0xA0, /// Uses `tag` f64_sub = 0xA1, /// Uses `tag` f64_mul = 0xA2, /// Uses `tag` f64_div = 0xA3, /// Uses `tag` f64_min = 0xA4, /// Uses `tag` f64_max = 0xA5, /// Uses `tag` f64_copysign = 0xA6, /// Uses `tag` i32_wrap_i64 = 0xA7, /// Uses `tag` i32_trunc_f32_s = 0xA8, /// Uses `tag` i32_trunc_f32_u = 0xA9, /// Uses `tag` i32_trunc_f64_s = 0xAA, /// Uses `tag` i32_trunc_f64_u = 0xAB, /// Uses `tag` i64_extend_i32_s = 0xAC, /// Uses `tag` i64_extend_i32_u = 0xAD, /// Uses `tag` i64_trunc_f32_s = 0xAE, /// Uses `tag` i64_trunc_f32_u = 0xAF, /// Uses `tag` i64_trunc_f64_s = 0xB0, /// Uses `tag` i64_trunc_f64_u = 0xB1, /// Uses `tag` f32_convert_i32_s = 0xB2, /// Uses `tag` f32_convert_i32_u = 0xB3, /// Uses `tag` f32_convert_i64_s = 0xB4, /// Uses `tag` f32_convert_i64_u = 0xB5, /// Uses `tag` f32_demote_f64 = 0xB6, /// Uses `tag` f64_convert_i32_s = 0xB7, /// Uses `tag` f64_convert_i32_u = 0xB8, /// Uses `tag` f64_convert_i64_s = 0xB9, /// Uses `tag` f64_convert_i64_u = 0xBA, /// Uses `tag` f64_promote_f32 = 0xBB, /// Uses `tag` i32_reinterpret_f32 = 0xBC, /// Uses `tag` i64_reinterpret_f64 = 0xBD, /// Uses `tag` f32_reinterpret_i32 = 0xBE, /// Uses `tag` f64_reinterpret_i64 = 0xBF, /// Uses `tag` i32_extend8_s = 0xC0, /// Uses `tag` i32_extend16_s = 0xC1, /// Uses `tag` i64_extend8_s = 0xC2, /// Uses `tag` i64_extend16_s = 0xC3, /// Uses `tag` i64_extend32_s = 0xC4, /// The instruction consists of a prefixed opcode. /// The prefixed opcode can be found at payload's index. /// /// The `data` field depends on the extension instruction and /// may contain additional data. misc_prefix, /// The instruction consists of a simd opcode. /// The actual simd-opcode is found at payload's index. /// /// The `data` field depends on the simd instruction and /// may contain additional data. simd_prefix, /// The instruction consists of an atomics opcode. /// The actual atomics-opcode is found at payload's index. /// /// The `data` field depends on the atomics instruction and /// may contain additional data. atomics_prefix = 0xFE, /// From a given wasm opcode, returns a MIR tag. pub fn fromOpcode(opcode: std.wasm.Opcode) Tag { return @as(Tag, @enumFromInt(@intFromEnum(opcode))); // Given `Opcode` is not present as a tag for MIR yet } /// Returns a wasm opcode from a given MIR tag. pub fn toOpcode(self: Tag) std.wasm.Opcode { return @as(std.wasm.Opcode, @enumFromInt(@intFromEnum(self))); } }; /// All instructions contain a 4-byte payload, which is contained within /// this union. `Tag` determines which union tag is active, as well as /// how to interpret the data within. pub const Data = union { /// Uses no additional data tag: void, /// Contains the result type of a block block_type: std.wasm.BlockType, /// Label: Each structured control instruction introduces an implicit label. /// Labels are targets for branch instructions that reference them with /// label indices. Unlike with other index spaces, indexing of labels /// is relative by nesting depth, that is, label 0 refers to the /// innermost structured control instruction enclosing the referring /// branch instruction, while increasing indices refer to those farther /// out. Consequently, labels can only be referenced from within the /// associated structured control instruction. label: u32, /// Local: The index space for locals is only accessible inside a function and /// includes the parameters of that function, which precede the local /// variables. local: u32, /// A 32-bit immediate value. imm32: i32, /// A 32-bit float value float32: f32, /// Index into `extra`. Meaning of what can be found there is context-dependent. payload: u32, ip_index: InternPool.Index, nav_index: InternPool.Nav.Index, intrinsic: Intrinsic, comptime { switch (builtin.mode) { .Debug, .ReleaseSafe => {}, .ReleaseFast, .ReleaseSmall => assert(@sizeOf(Data) == 4), } } }; }; pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.instructions.deinit(gpa); gpa.free(mir.extra); gpa.free(mir.locals); mir.uavs.deinit(gpa); mir.indirect_function_set.deinit(gpa); mir.func_tys.deinit(gpa); mir.* = undefined; } pub fn lower(mir: *const Mir, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) std.mem.Allocator.Error!void { const gpa = wasm.base.comp.gpa; // Write the locals in the prologue of the function body. try code.ensureUnusedCapacity(gpa, 5 + mir.locals.len * 6 + 38); var w: std.Io.Writer = .fixed(code.unusedCapacitySlice()); w.writeLeb128(@as(u32, @intCast(mir.locals.len))) catch unreachable; for (mir.locals) |local| { w.writeLeb128(@as(u32, 1)) catch unreachable; w.writeByte(@intFromEnum(local)) catch unreachable; } // Stack management section of function prologue. const stack_alignment = mir.prologue.flags.stack_alignment; if (stack_alignment.toByteUnits()) |align_bytes| { const sp_global: Wasm.GlobalIndex = .stack_pointer; // load stack pointer w.writeByte(@intFromEnum(std.wasm.Opcode.global_get)) catch unreachable; w.writeUleb128(@intFromEnum(sp_global)) catch unreachable; // store stack pointer so we can restore it when we return from the function w.writeByte(@intFromEnum(std.wasm.Opcode.local_tee)) catch unreachable; w.writeUleb128(mir.prologue.sp_local) catch unreachable; // get the total stack size const aligned_stack: i32 = @intCast(stack_alignment.forward(mir.prologue.stack_size)); w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)) catch unreachable; w.writeSleb128(aligned_stack) catch unreachable; // subtract it from the current stack pointer w.writeByte(@intFromEnum(std.wasm.Opcode.i32_sub)) catch unreachable; // Get negative stack alignment const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1; w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)) catch unreachable; w.writeSleb128(neg_stack_align) catch unreachable; // Bitwise-and the value to get the new stack pointer to ensure the // pointers are aligned with the abi alignment. w.writeByte(@intFromEnum(std.wasm.Opcode.i32_and)) catch unreachable; // The bottom will be used to calculate all stack pointer offsets. w.writeByte(@intFromEnum(std.wasm.Opcode.local_tee)) catch unreachable; w.writeUleb128(mir.prologue.bottom_stack_local) catch unreachable; // Store the current stack pointer value into the global stack pointer so other function calls will // start from this value instead and not overwrite the current stack. w.writeByte(@intFromEnum(std.wasm.Opcode.global_set)) catch unreachable; w.writeUleb128(@intFromEnum(sp_global)) catch unreachable; } code.items.len += w.end; var emit: Emit = .{ .mir = mir.*, .wasm = wasm, .code = code, }; try emit.lowerToCode(); } pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data: T, end: usize } { const fields = std.meta.fields(T); var i: usize = index; var result: T = undefined; inline for (fields) |field| { @field(result, field.name) = switch (field.type) { u32 => self.extra[i], i32 => @bitCast(self.extra[i]), Wasm.UavsObjIndex, Wasm.UavsExeIndex, InternPool.Nav.Index, InternPool.Index, => @enumFromInt(self.extra[i]), else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), }; i += 1; } return .{ .data = result, .end = i }; } pub const JumpTable = struct { /// Length of the jump table and the amount of entries it contains (includes default) length: u32, }; pub const Imm64 = struct { msb: u32, lsb: u32, pub fn init(full: u64) Imm64 { return .{ .msb = @truncate(full >> 32), .lsb = @truncate(full), }; } pub fn toInt(i: Imm64) u64 { return (@as(u64, i.msb) << 32) | @as(u64, i.lsb); } }; pub const Float64 = struct { msb: u32, lsb: u32, pub fn init(f: f64) Float64 { const int: u64 = @bitCast(f); return .{ .msb = @truncate(int >> 32), .lsb = @truncate(int), }; } pub fn toInt(f: Float64) u64 { return (@as(u64, f.msb) << 32) | @as(u64, f.lsb); } }; pub const MemArg = struct { offset: u32, alignment: u32, }; pub const UavRefOff = struct { value: InternPool.Index, offset: i32, }; pub const NavRefOff = struct { nav_index: InternPool.Nav.Index, offset: i32, }; /// Maps a source line with wasm bytecode pub const DbgLineColumn = struct { line: u32, column: u32, }; /// Tag names exactly match the corresponding symbol name. pub const Intrinsic = enum(u32) { __addhf3, __addtf3, __addxf3, __ashlti3, __ashrti3, __bitreversedi2, __bitreversesi2, __bswapdi2, __bswapsi2, __ceilh, __ceilx, __cosh, __cosx, __divhf3, __divtf3, __divti3, __divxf3, __eqtf2, __eqxf2, __exp2h, __exp2x, __exph, __expx, __extenddftf2, __extenddfxf2, __extendhfsf2, __extendhftf2, __extendhfxf2, __extendsftf2, __extendsfxf2, __extendxftf2, __fabsh, __fabsx, __fixdfdi, __fixdfsi, __fixdfti, __fixhfdi, __fixhfsi, __fixhfti, __fixsfdi, __fixsfsi, __fixsfti, __fixtfdi, __fixtfsi, __fixtfti, __fixunsdfdi, __fixunsdfsi, __fixunsdfti, __fixunshfdi, __fixunshfsi, __fixunshfti, __fixunssfdi, __fixunssfsi, __fixunssfti, __fixunstfdi, __fixunstfsi, __fixunstfti, __fixunsxfdi, __fixunsxfsi, __fixunsxfti, __fixxfdi, __fixxfsi, __fixxfti, __floatdidf, __floatdihf, __floatdisf, __floatditf, __floatdixf, __floatsidf, __floatsihf, __floatsisf, __floatsitf, __floatsixf, __floattidf, __floattihf, __floattisf, __floattitf, __floattixf, __floatundidf, __floatundihf, __floatundisf, __floatunditf, __floatundixf, __floatunsidf, __floatunsihf, __floatunsisf, __floatunsitf, __floatunsixf, __floatuntidf, __floatuntihf, __floatuntisf, __floatuntitf, __floatuntixf, __floorh, __floorx, __fmah, __fmax, __fmaxh, __fmaxx, __fminh, __fminx, __fmodh, __fmodx, __getf2, __gexf2, __gttf2, __gtxf2, __letf2, __lexf2, __log10h, __log10x, __log2h, __log2x, __logh, __logx, __lshrti3, __lttf2, __ltxf2, __modti3, __mulhf3, __mulodi4, __muloti4, __multf3, __multi3, __mulxf3, __netf2, __nexf2, __roundh, __roundx, __sinh, __sinx, __sqrth, __sqrtx, __subhf3, __subtf3, __subxf3, __tanh, __tanx, __trunch, __truncsfhf2, __trunctfdf2, __trunctfhf2, __trunctfsf2, __trunctfxf2, __truncx, __truncxfdf2, __truncxfhf2, __truncxfsf2, __udivti3, __umodti3, ceilq, cos, cosf, cosq, exp, exp2, exp2f, exp2q, expf, expq, fabsq, floorq, fma, fmaf, fmaq, fmax, fmaxf, fmaxq, fmin, fminf, fminq, fmod, fmodf, fmodq, log, log10, log10f, log10q, log2, log2f, log2q, logf, logq, roundq, sin, sinf, sinq, sqrtq, tan, tanf, tanq, truncq, };