Merge branch 'master' into asm_immediate_constant

This commit is contained in:
gingerBill authored and GitHub committed 2026-08-25 15:28:06 +01:00
commit 2f8ddd560b
52 files changed
+5374 -3490

No files matched your search

+1 -1
View File
@@ -561,7 +561,7 @@ uint_max :: proc(n: uint, gen := context.random_generator) -> (val: uint) {
Generates a random unsigned 32 bit value in the range `[lo, hi)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- lo: The lower bound of the generated number, this value is inclusice
- lo: The lower bound of the generated number, this value is inclusive
- hi: The upper bound of the generated number, this value is exclusive
Returns:
+189 -73
View File
@@ -19,6 +19,38 @@ import win "core:sys/windows"
@(private="package")
_FULLY_SUPPORTED :: true
// Poll is driven by AFD, the socket driver underneath winsock.
// `WSAEventSelect` is edge triggered (`FD_WRITE` is only recorded again after a
// send fails with WOULDBLOCK) and neither `select` nor `WSAPoll` reports send
// buffer space, so neither can give the level triggered readiness `poll` promises.
// AFD also completes on the IOCP, which makes a poll an ordinary overlapped operation.
IOCTL_AFD_POLL :: 0x00012024
SIO_BASE_HANDLE :: win.DWORD(0x48000022)
AFD_POLL_RECEIVE :: 0x0001
AFD_POLL_RECEIVE_EXPEDITED :: 0x0002
AFD_POLL_SEND :: 0x0004
AFD_POLL_DISCONNECT :: 0x0008
AFD_POLL_ABORT :: 0x0010
AFD_POLL_LOCAL_CLOSE :: 0x0020
AFD_POLL_ACCEPT :: 0x0080
AFD_POLL_CONNECT_FAIL :: 0x0100
AFD_Poll_Handle_Info :: struct {
handle: win.HANDLE,
events: win.ULONG,
status: win.NTSTATUS,
}
AFD_Poll_Info :: struct {
timeout: i64,
number_of_handles: win.ULONG,
exclusive: win.ULONG,
handles: [1]AFD_Poll_Handle_Info,
}
afd_device_name := [?]u16{'\\','D','e','v','i','c','e','\\','A','f','d','\\','E','n','d','p','o','i','n','t'}
@(private="package")
_Event_Loop :: struct {
timeouts: avl.Tree(^Operation),
@@ -88,7 +120,7 @@ _Timeout :: struct {
@(private="package")
_Poll :: struct {
wait_handle: win.HANDLE,
info: AFD_Poll_Info,
}
@(private="package")
@@ -165,15 +197,7 @@ __tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> (err: General_Error) {
if pool.num_outstanding(&l.operation_pool) == 0 { return nil }
actual_timeout := win.INFINITE
if queue.len(l.completed) > 0 || mpsc_count(&l.completed_oob) > 0 {
actual_timeout = 0
} else if timeout >= 0 {
actual_timeout = win.DWORD(timeout / time.Millisecond)
}
if nt, ok := next_timeout.?; ok {
actual_timeout = min(actual_timeout, win.DWORD(nt / time.Millisecond))
}
actual_timeout := compute_timeout(l, timeout, next_timeout)
if actual_timeout > 0 {
sync.atomic_store_explicit(&l.state, .Sleeping, .Release)
@@ -181,7 +205,6 @@ __tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> (err: General_Error) {
// There could be a race condition where we go sleeping at the same time as things get queued
// and a wakeup isn't done because the state is not .Sleeping yet.
// So after sleeping we first check our queues.
for {
op := (^Operation)(mpsc_dequeue(&l.queue))
if op == nil { break }
@@ -193,6 +216,10 @@ __tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> (err: General_Error) {
if op == nil { break }
handle_completed(op)
}
// The drains can add timeouts, and `timeout_exec` only puts those in
// `l.timeouts` without posting anything
actual_timeout = compute_timeout(l, timeout, check_timeouts(l))
}
for {
@@ -228,7 +255,7 @@ __tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> (err: General_Error) {
handle_completed(op)
} else {
op_l := op.l
for !mpsc_enqueue(&op.l.completed_oob, op) {
for !mpsc_enqueue(&op_l.completed_oob, op) {
warn("oob queue filled up, QUEUE_SIZE may need increasing")
_wake_up(op_l)
win.SwitchToThread()
@@ -246,8 +273,35 @@ __tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> (err: General_Error) {
actual_timeout = 0
}
// A wake, or another loop routing a completion to us, can leave work queued.
// Handle it here instead of waiting for the caller to tick again.
for {
op := (^Operation)(mpsc_dequeue(&l.queue))
if op == nil { break }
_exec(op)
}
for {
op := (^Operation)(mpsc_dequeue(&l.completed_oob))
if op == nil { break }
handle_completed(op)
}
return nil
compute_timeout :: proc(l: ^Event_Loop, timeout: time.Duration, next_timeout: Maybe(time.Duration)) -> win.DWORD {
actual: win.DWORD = win.INFINITE
if queue.len(l.completed) > 0 || mpsc_count(&l.completed_oob) > 0 {
actual = 0
} else if timeout >= 0 {
actual = win.DWORD(timeout / time.Millisecond)
}
if nt, ok := next_timeout.?; ok {
actual = min(actual, win.DWORD(nt / time.Millisecond))
}
return actual
}
check_timeouts :: proc(l: ^Event_Loop) -> (expires: Maybe(time.Duration)) {
curr := l.now
@@ -677,19 +731,6 @@ _remove :: proc(target: ^Operation) {
target._impl.timeout = (^Operation)(REMOVED)
switch target.type {
case .Poll:
win.UnregisterWaitEx(target.poll._impl.wait_handle, nil)
target.poll._impl.wait_handle = nil
ok := win.PostQueuedCompletionStatus(
g.iocp,
0,
0,
&target._impl.over,
)
ensure(ok == true, "unexpected PostQueuedCompletionStatus error")
return
case .Timeout:
if avl.remove_value(&target.l.timeouts, target) {
debug("removed timeout directly")
@@ -705,6 +746,17 @@ _remove :: proc(target: ^Operation) {
// Synchronous ops, picked up in handler.
return
case .Poll:
// The poll may have completed already, with its completion queued but not yet
// handled, `NOT_FOUND` is expected rather than exceptional.
if !win.CancelIoEx(g.afd, &target._impl.over) {
#partial switch win.System_Error(win.GetLastError()) {
case .NOT_FOUND:
// nop
case: assert(false, "unexpected CancelIoEx error")
}
}
case .Accept, .Dial, .Read, .Recv, .Send, .Write, .Send_File:
if is_pending(target._impl.over) {
handle := operation_handle(target)
@@ -802,6 +854,7 @@ g: struct{
mu: sync.Mutex,
refs: int,
iocp: win.HANDLE,
afd: win.HANDLE,
err: General_Error,
}
@@ -817,6 +870,36 @@ g_ref :: proc() -> General_Error {
if g.iocp == nil {
g.err = General_Error(win.GetLastError())
}
if g.err != nil { return g.err }
// A handle on the socket driver, used to poll sockets for readiness.
iosb: win.IO_STATUS_BLOCK
status := win.NtCreateFile(
&g.afd,
win.SYNCHRONIZE,
&{
Length = size_of(win.OBJECT_ATTRIBUTES),
ObjectName = &{
Length = u16(len(afd_device_name)*2),
MaximumLength = u16(len(afd_device_name)*2),
Buffer = raw_data(afd_device_name[:]),
},
},
&iosb,
nil,
0,
win.FILE_SHARE_READ|win.FILE_SHARE_WRITE,
win.FILE_OPEN,
0,
nil,
0,
)
if syserr := win.System_Error(win.RtlNtStatusToDosError(status)); syserr != .SUCCESS {
g.err = General_Error(syserr)
} else if win.CreateIoCompletionPort(g.afd, g.iocp, 0, 0) != g.iocp {
g.err = General_Error(win.GetLastError())
}
}
sync.atomic_add(&g.refs, 1)
@@ -828,6 +911,7 @@ g_unref :: proc() {
sync.guard(&g.mu)
if sync.atomic_sub(&g.refs, 1) == 1 {
if g.afd != nil { win.CloseHandle(g.afd) }
win.CloseHandle(g.iocp)
g.err = nil
}
@@ -850,7 +934,7 @@ operation_handle :: proc(op: ^Operation) -> win.HANDLE {
case .Recv: return win.HANDLE(uintptr(net.any_socket_to_socket(op.recv.socket)))
case .Send: return win.HANDLE(uintptr(net.any_socket_to_socket(op.send.socket)))
case .Send_File: return win.HANDLE(uintptr(net.any_socket_to_socket(op.sendfile.socket)))
case .Poll: return win.HANDLE(uintptr(net.any_socket_to_socket(op.poll.socket)))
case .Poll: return g.afd
case .Stat: return win.HANDLE(uintptr(op.stat.handle))
case .Timeout, .Open, ._Splice, ._Link_Timeout, ._Remove, .None:
@@ -925,6 +1009,9 @@ accept_exec :: proc(op: ^Operation) -> Op_Result {
return .Pending
} else if op._impl.over.Internal == nil {
op.accept.err = net._accept_error()
} else {
link_timeout(op, op.accept.expires)
return .Pending
}
}
@@ -1026,6 +1113,9 @@ dial_exec :: proc(op: ^Operation) -> (result: Op_Result) {
return .Pending
} else if op._impl.over.Internal == nil {
op.dial.err = net._dial_error()
} else {
link_timeout(op, op.dial.expires)
return .Pending
}
}
@@ -1085,6 +1175,13 @@ read_exec :: proc(op: ^Operation) -> Op_Result {
return .Pending
}
op.read.err = FS_Error(err)
} else {
// The read completed synchronously with a failure status. `FILE_SKIP_COMPLETION_PORT_ON_SUCCESS`
// only suppresses the completion packet on success, so one is still queued for
// this. Returning `.Done` here would complete the operation a second time, on an
// Operation that has already been recycled into the pool.
link_timeout(op, op.read.expires)
return .Pending
}
}
@@ -1159,6 +1256,9 @@ write_exec :: proc(op: ^Operation) -> Op_Result {
return .Pending
}
op.write.err = FS_Error(err)
} else {
link_timeout(op, op.write.expires)
return .Pending
}
}
@@ -1252,6 +1352,9 @@ recv_exec :: proc(op: ^Operation) -> Op_Result {
case TCP_Socket: op.recv.err = net._tcp_recv_error()
case UDP_Socket: op.recv.err = net._udp_recv_error()
}
} else {
link_timeout(op, op.recv.expires)
return .Pending
}
}
@@ -1370,6 +1473,9 @@ send_exec :: proc(op: ^Operation) -> Op_Result {
case TCP_Socket: op.send.err = net._tcp_send_error()
case UDP_Socket: op.send.err = net._udp_send_error()
}
} else {
link_timeout(op, op.send.expires)
return .Pending
}
}
@@ -1459,6 +1565,9 @@ sendfile_exec :: proc(op: ^Operation) -> Op_Result {
return .Pending
} else if op._impl.over.Internal == nil {
op.sendfile.err = net._tcp_send_error()
} else {
link_timeout(op, op.sendfile.expires)
return .Pending
}
}
@@ -1505,84 +1614,91 @@ sendfile_callback :: proc(op: ^Operation) -> Op_Result {
@(require_results)
poll_exec :: proc(op: ^Operation) -> Op_Result {
assert(op.type == .Poll)
op._impl.over = {} // Operations are recycled, clear stale state from a previous use.
events: i32 = win.FD_CLOSE
events: win.ULONG = AFD_POLL_ABORT|AFD_POLL_DISCONNECT|AFD_POLL_LOCAL_CLOSE|AFD_POLL_CONNECT_FAIL
switch op.poll.event {
case .Send: events |= win.FD_WRITE|win.FD_CONNECT
case .Receive: events |= win.FD_READ|win.FD_ACCEPT
case .Receive: events |= AFD_POLL_RECEIVE|AFD_POLL_RECEIVE_EXPEDITED|AFD_POLL_ACCEPT
case .Send: events |= AFD_POLL_SEND
case:
op.poll.result = .Invalid_Argument
return .Done
}
op._impl.over.hEvent = win.WSACreateEvent()
if win.WSAEventSelect(
// AFD needs the socket underneath any layered service providers.
base: win.SOCKET
bytes: win.DWORD
if win.WSAIoctl(
win.SOCKET(net.any_socket_to_socket(op.poll.socket)),
op._impl.over.hEvent,
events,
SIO_BASE_HANDLE,
nil, 0,
&base, size_of(base),
&bytes, nil, nil,
) != 0 {
#partial switch win.System_Error(win.GetLastError()) {
#partial switch win.System_Error(win.WSAGetLastError()) {
case .WSAEINVAL, .WSAENOTSOCK: op.poll.result = .Invalid_Argument
case: op.poll.result = .Error
}
return .Done
}
timeout := win.INFINITE
// A negative timeout is relative, in 100ns units.
timeout := max(i64)
if op.poll.expires != {} {
diff := max(0, time.diff(op.l.now, op.poll.expires))
timeout = win.DWORD(diff / time.Millisecond)
timeout = -i64(diff / 100)
}
ok := win.RegisterWaitForSingleObject(
&op.poll._impl.wait_handle,
op._impl.over.hEvent,
wait_callback,
op,
timeout,
win.WT_EXECUTEINWAITTHREAD|win.WT_EXECUTEONLYONCE,
op.poll._impl.info = {
timeout = timeout,
number_of_handles = 1,
handles = {{handle = win.HANDLE(uintptr(base)), events = events}},
}
// The OVERLAPPED doubles as the IO_STATUS_BLOCK, their first two fields line up.
status := win.NtDeviceIoControlFile(
g.afd,
nil,
nil,
&op._impl.over,
win.PIO_STATUS_BLOCK(rawptr(&op._impl.over)),
IOCTL_AFD_POLL,
&op.poll._impl.info,
size_of(AFD_Poll_Info),
&op.poll._impl.info,
size_of(AFD_Poll_Info),
)
ensure(ok == true, "unexpected RegisterWaitForSingleObject error")
return .Pending
wait_callback :: proc "system" (lpParameter: win.PVOID, TimerOrWaitFired: win.BOOLEAN) {
op := (^Operation)(lpParameter)
assert_contextless(op.type == .Poll)
if TimerOrWaitFired {
op.poll.result = .Timeout
}
ok := win.PostQueuedCompletionStatus(
g.iocp,
0,
0,
&op._impl.over,
)
ensure_contextless(ok == true, "unexpected PostQueuedCompletionStatus error")
// The AFD handle is not set to skip completion on success, so a completion is
// queued even when this finishes synchronously.
#partial switch win.System_Error(win.RtlNtStatusToDosError(status)) {
case .SUCCESS, .IO_PENDING:
return .Pending
case:
op.poll.result = .Error
return .Done
}
}
poll_callback :: proc(op: ^Operation) {
assert(op.type == .Poll)
if op._impl.over.hEvent != nil {
win.WSACloseEvent(op._impl.over.hEvent)
}
if op.poll._impl.wait_handle != nil {
win.UnregisterWaitEx(op.poll._impl.wait_handle, nil)
}
if op.poll.result != nil {
return
}
_, err := get_result(op._impl.over)
#partial switch err {
case .SUCCESS:
case:
// AFD reports a timeout by coming back with no handles.
if op.poll._impl.info.number_of_handles == 0 {
op.poll.result = .Timeout
return
}
if _, err := get_result(op._impl.over); err != .SUCCESS {
op.poll.result = .Error
return
}
if op.poll._impl.info.handles[0].events & (AFD_POLL_ABORT|AFD_POLL_CONNECT_FAIL) != 0 {
op.poll.result = .Error
}
}
+12
View File
@@ -1626,6 +1626,9 @@ Poll a socket for readiness.
NOTE: this is provided to help with "legacy" APIs that require polling behavior.
If you can avoid it and use the other procs in this package, do so.
NOTE: on Windows only one poll per socket is delivered, a second poll on the same
socket does not complete.
Any user data can be set on the returned operation's `user_data` field.
Polymorphic variants for type safe user data are available under `poll_poly`, `poll_poly2`, and `poll_poly3`.
@@ -1656,6 +1659,9 @@ Poll a socket for readiness.
NOTE: this is provided to help with "legacy" APIs that require polling behavior.
If you can avoid it and use the other procs in this package, do so.
NOTE: on Windows only one poll per socket is delivered, a second poll on the same
socket does not complete.
This procedure uses polymorphism for type safe user data up to a certain size.
Inputs:
@@ -1690,6 +1696,9 @@ Poll a socket for readiness.
NOTE: this is provided to help with "legacy" APIs that require polling behavior.
If you can avoid it and use the other procs in this package, do so.
NOTE: on Windows only one poll per socket is delivered, a second poll on the same
socket does not complete.
This procedure uses polymorphism for type safe user data up to a certain size.
Inputs:
@@ -1725,6 +1734,9 @@ Poll a socket for readiness.
NOTE: this is provided to help with "legacy" APIs that require polling behavior.
If you can avoid it and use the other procs in this package, do so.
NOTE: on Windows only one poll per socket is delivered, a second poll on the same
socket does not complete.
This procedure uses polymorphism for type safe user data up to a certain size.
Inputs:
@@ -120,12 +120,12 @@ main :: proc() {
strings.write_string(&sb, "\n")
strings.write_string(&sb, """
enum ClobberFFlags : u8 {
ClobberFFlag_NV = 1<<0, // invalid operation
ClobberFFlag_DZ = 1<<1, // divide by zero
ClobberFFlag_OF = 1<<2, // overflow
ClobberFFlag_UF = 1<<3, // underflow
ClobberFFlag_NX = 1<<4, // inexact
enum ClobberFlags : u8 {
ClobberFlag_NV = 1<<0, // invalid operation
ClobberFlag_DZ = 1<<1, // divide by zero
ClobberFlag_OF = 1<<2, // overflow
ClobberFlag_UF = 1<<3, // underflow
ClobberFlag_NX = 1<<4, // inexact
};
enum ClobberRegs : u8 {
@@ -174,9 +174,28 @@ main :: proc() {
return \"<reg>\";
}
u16 flags_from_name(String const &name) {
static const struct { String name; ClobberFlags flag; } table[] = {
// flags: accrued FP exception flags (fcsr[4:0])
{str_lit(\"nx\"), ClobberFlag_NX}, // Inexact
{str_lit(\"uf\"), ClobberFlag_UF}, // Underflow
{str_lit(\"of\"), ClobberFlag_OF}, // Overflow
{str_lit(\"dz\"), ClobberFlag_DZ}, // Divide by Zero
{str_lit(\"nv\"), ClobberFlag_NV}, // Invalid Operation
};
for (auto const &t : table) {
if (name == t.name) {
return cast(u16)t.flag;
}
}
return 0;
}
i32 flag_bit_from_name(String const &name, i32 *width_) {
static const struct { String name; i32 bit; } table[] = {
// fflags: accrued FP exception flags (fcsr[4:0])
// flags: accrued FP exception flags (fcsr[4:0])
{str_lit(\"nx\"), 0}, // Inexact
{str_lit(\"uf\"), 1}, // Underflow
{str_lit(\"of\"), 2}, // Overflow
@@ -207,14 +226,14 @@ main :: proc() {
OperandSet read; // operand slots whose register/CSR/mem-base is read
ClobberRegs implicit_wr; // implicit reg writes (ra on C.JAL/C.JALR)
ClobberRegs implicit_rd; // implicit reg reads (sp on the *SP forms)
ClobberFFlags fflags_wr; // accrued exception flags this op may raise
ClobberFlags flags_wr; // accrued exception flags this op may raise
bool reads_frm; // consumes the dynamic rounding mode from fcsr
bool writes_mem;
bool reads_mem;
SideEffectFlags side_effects;
bool implies_clobber_flags() const {
return (fflags_wr != 0);
return (flags_wr != 0);
}
bool implies_clobber_memory() const {
return writes_mem || reads_mem ||
@@ -236,6 +255,17 @@ main :: proc() {
bool is_conditional() const {
return has_control();
}
bool is_nondeterministic() const {
return false;
}
bool has_implicit_mem() const {
if (!writes_mem && !reads_mem) {
return false;
}
u16 implicit = cast(u16)implicit_rd | cast(u16)implicit_wr;
bool is_atomic = false; // TODO(bill): Add ATOMIC flag to SideEffectFlags in the original INSTRUCTION_TABLE
return (implicit & (ClobberReg_SP)) != 0 || is_atomic;
}
};
void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) {
@@ -273,6 +303,17 @@ main :: proc() {
u16 csr; // CSR address when a src slot is AliasSrc_CSR_LIT
u8 nargs; // operands the user supplies (ARG0..<ARGn)
bool rv32_only; // base gate (the *h counter reads)
// Nondeterministic iff this is a CSR access whose CSR operand names a counter/timer/entropy register (extension-gated; absent CSRs never match)
bool is_nondeterministic() const {
if (csr == 0x015) return true; // seed (Zkr)
if (0xC00 <= csr && csr <= 0xC1F) return true; // cycle/time/instret + hpm (unpriv)
if (0xC80 <= csr && csr <= 0xC9F) return true; // rv32 high halves (unpriv)
if (0xB00 <= csr && csr <= 0xB1F) return true; // mcycle/minstret + mhpm
if (0xB80 <= csr && csr <= 0xB9F) return true; // rv32 high halves (machine)
return false;
}
};
enum PseudoMnemonic : u16 {
@@ -298,10 +339,10 @@ main :: proc() {
PseudoAlias *pa = (PseudoAlias *)raw_pseudo_aliases;
return pa[pm];
}
""")
strings.write_string(&sb, "\tstatic String const pseudo_mnemonic_strings[PSEUDO_MNEMONIC_COUNT];\n")
strings.write_string(&sb, "\n")
static String const pseudo_mnemonic_strings[PSEUDO_MNEMONIC_COUNT];
\n\n
""")
}
@@ -783,6 +824,37 @@ main :: proc() {
}
""")
strings.write_string(&sb, "\n")
strings.write_string(&sb, """
AsmOperandConstraint operand_value_constraint(u16 m, int op) const {
switch (m) {
case M_SLLI: case M_SRLI: case M_SRAI:
if (op == 2) return {AsmOperandConstraint_ShiftCount, /*XLEN*/-1};
break;
case M_DIV: case M_DIVU: case M_REM: case M_REMU:
if (op == 2) return {AsmOperandConstraint_NonZeroDivisor, -1};
break;
}
return {AsmOperandConstraint_None, -1};
}
""")
strings.write_string(&sb, """
bool is_self_zeroing_idiom(u16 m) const {
switch (m) {
case M_XOR:
case M_SUB:
case M_SUBW:
case M_SLT:
case M_SLTU:
case M_ANDN:
return true;
}
return false;
}
""")
strings.write_string(&sb, "\n};\n")
strings.write_string(&sb, "\n\n\n")
+1
View File
@@ -24,6 +24,7 @@ Side_Effect :: enum u8 {
PRIVILEGED, // requires CPL0 / reads-writes supervisor machine state
CONTROL, // alters control flow (writes RIP): branches, calls, returns
CET, // control-flow-enforcement: landing pads, shadow-stack ops
NONDETERMINISTIC,
}
Side_Effects :: distinct bit_set[Side_Effect; u16]
+1
View File
@@ -53,6 +53,7 @@ Mnemonic :: enum u16 {
SHL,
SHR,
SAR,
SAL,
ROL,
ROR,
RCL,
@@ -168,6 +168,7 @@ main :: proc() {
SideEffectFlag_PRIVILEGED = 1<<7, // requires CPL0 / reads-writes supervisor machine state
SideEffectFlag_CONTROL = 1<<8, // alters control flow (writes RIP): branches, calls, returns
SideEffectFlag_CET = 1<<9, // control-flow-enforcement: landing pads, shadow-stack ops
SideEffectFlag_NONDETERMINISTIC = 1<<10,
};
enum ClobberRegs : u16 {
ClobberReg_RAX = 1<<0,
@@ -236,6 +237,27 @@ main :: proc() {
return \"<reg>\";
}
u16 flag_from_name(String const &name) {
static const struct {String name; ClobberFlags flag; } table[] = {
{str_lit(\"c\"), ClobberFlag_CF}, // Carry
{str_lit(\"p\"), ClobberFlag_PF}, // Parity
{str_lit(\"a\"), ClobberFlag_AF}, // Auxiliary Carry
{str_lit(\"z\"), ClobberFlag_ZF}, // Zero
{str_lit(\"s\"), ClobberFlag_SF}, // Sign
{str_lit(\"t\"), ClobberFlag_TF}, // Trap
{str_lit(\"i\"), ClobberFlag_IF}, // Interrupt Enable
{str_lit(\"d\"), ClobberFlag_DF}, // Direction
{str_lit(\"o\"), ClobberFlag_OF}, // Overflow
};
for (auto const &t : table) {
if (name == t.name) {
return cast(u16)t.flag;
}
}
return 0;
}
i32 flag_bit_from_name(String const &name, i32 *width_) {
static const struct {String name; i32 bit; } table[] = {
{str_lit(\"c\"), 0}, // Carry
@@ -305,7 +327,8 @@ main :: proc() {
SideEffectFlag_HALT |
SideEffectFlag_PRIVILEGED |
SideEffectFlag_CONTROL |
SideEffectFlag_CET;
SideEffectFlag_CET |
SideEffectFlag_NONDETERMINISTIC;
// NOTE: SideEffectFlag_HINT deliberately excluded — inert, may be DCE'd.
return ((side_effects & VOLATILE_SE) != 0);
}
@@ -323,6 +346,16 @@ main :: proc() {
bool is_conditional() const {
return has_control() && (cast(u16)flags_rd != 0);
}
bool is_nondeterministic() const {
return (cast(u16)side_effects & SideEffectFlag_NONDETERMINISTIC) != 0;
}
bool has_implicit_mem() const {
if (!writes_mem && !reads_mem) {
return false;
}
u16 implicit = cast(u16)implicit_rd | cast(u16)implicit_wr;
return (implicit & (ClobberReg_RSP|ClobberReg_RSI|ClobberReg_RDI|ClobberReg_RBX)) != 0;
}
};
void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) {
@@ -350,12 +383,17 @@ main :: proc() {
AliasSrc_LIT,
};
// NOTE(bill): These are completely dummy things as it is only needed by RISC-V and not x86
struct PseudoAlias {
Mnemonic target; // real instruction emitted
AliasSrc src[4]; // how to fill target's four operand slots
i16 lit; // immediate when a src slot is AliasSrc_LIT
u16 csr; // CSR address when a src slot is AliasSrc_CSR_LIT
u8 nargs; // operands the user supplies (ARG0..<ARGn)
Mnemonic target; // real instruction emitted
AliasSrc src[4]; // how to fill target's four operand slots
i16 lit; // immediate when a src slot is AliasSrc_LIT
u16 csr; // CSR address when a src slot is AliasSrc_CSR_LIT
u8 nargs; // operands the user supplies (ARG0..<ARGn)
bool is_nondeterministic() const {
return false;
}
};
enum PseudoMnemonic : u16 {
PM_INVALID,
@@ -790,6 +828,62 @@ main :: proc() {
}
""")
strings.write_string(&sb, "\n")
strings.write_string(&sb, """
AsmOperandConstraint operand_value_constraint(u16 m, int op) const {
switch (m) {
case M_SHL: case M_SHR: case M_SAR: case M_SAL:
case M_ROL: case M_ROR: case M_RCL: case M_RCR:
if (op == 1) return {AsmOperandConstraint_ShiftCount, /*width_operand*/0};
break;
case M_DIV: case M_IDIV:
if (op == 0) return {AsmOperandConstraint_NonZeroDivisor, -1};
break;
}
return {AsmOperandConstraint_None, -1};
}
""")
strings.write_string(&sb, """
bool is_self_zeroing_idiom(u16 m) const {
switch (m) {
// integer xor / sub: x ^ x == 0, x - x == 0
case M_XOR:
case M_SUB:
// SSE/AVX bitwise xor of a register with itself
case M_PXOR:
case M_XORPS:
case M_XORPD:
case M_VPXOR:
case M_VXORPS:
case M_VXORPD:
// packed integer subtract: psub x, x == 0
case M_PSUBB:
case M_PSUBW:
case M_PSUBD:
case M_PSUBQ:
case M_VPSUBB:
case M_VPSUBW:
case M_VPSUBD:
case M_VPSUBQ:
// andnot of a value with itself: (~x) & x == 0
case M_ANDN: // BMI1 GPR: andn dst, a, a
case M_ANDNPS:
case M_ANDNPD:
case M_PANDN:
case M_VANDNPS:
case M_VANDNPD:
case M_VPANDN:
return true;
}
return false;
}
""")
strings.write_string(&sb, "\n};\n")
strings.write_string(&sb, "\n\n\n")
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -87,10 +87,10 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.MOV, {.RM16, .R16, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x89, 0, {}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.RM32, .R32, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x89, 0, {}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.RM64, .R64, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x89, 0, {force_rex_w=true}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8A, 0, {}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8B, 0, {}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8B, 0, {}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8B, 0, {force_rex_w=true}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8A, 0, {}}, {written={0}, read={1}, reads_mem=true}},
{{.MOV, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8B, 0, {}}, {written={0}, read={1}, reads_mem=true}},
{{.MOV, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8B, 0, {}}, {written={0}, read={1}, reads_mem=true}},
{{.MOV, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8B, 0, {force_rex_w=true}}, {written={0}, read={1}, reads_mem=true}},
{{.MOV, {.R8, .IMM8, .NONE, .NONE}, {.OP_R, .IB, .NONE, .NONE}, 0xB0, 0, {}}, {written={0}, read={1}}},
{{.MOV, {.R16, .IMM16, .NONE, .NONE}, {.OP_R, .IW, .NONE, .NONE}, 0xB8, 0, {}}, {written={0}, read={1}}},
{{.MOV, {.R32, .IMM32, .NONE, .NONE}, {.OP_R, .ID, .NONE, .NONE}, 0xB8, 0, {}}, {written={0}, read={1}}},
@@ -109,8 +109,8 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.MOV, {.MOFFS64, .RAX_IMPL, .NONE, .NONE}, {.IQ, .IMPL, .NONE, .NONE}, 0xA3, 0, {force_rex_w=true}}, {written={0}, implicit_rd={.RAX}, writes_mem=true, reads_mem=true}},
{{.MOV, {.RM16, .SREG, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x8C, 0, {}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.RM64, .SREG, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x8C, 0, {force_rex_w=true}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.SREG, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8E, 0, {}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.SREG, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8E, 0, {force_rex_w=true}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOV, {.SREG, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8E, 0, {}}, {written={0}, read={1}, reads_mem=true}},
{{.MOV, {.SREG, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x8E, 0, {force_rex_w=true}}, {written={0}, read={1}, reads_mem=true}},
{{.MOV, {.R64, .CR, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x20, 0, {esc=._0F}}, {written={0}, read={1}}},
{{.MOV, {.CR, .R64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x22, 0, {esc=._0F}}, {written={0}, read={1}}},
{{.MOV, {.R64, .DR, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x21, 0, {esc=._0F}}, {written={0}, read={1}}},
@@ -182,10 +182,10 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.ADD, {.RM16, .R16, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x01, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.ADD, {.RM32, .R32, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x01, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.ADD, {.RM64, .R64, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x01, 0, {force_rex_w=true, lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.ADD, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x02, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.ADD, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x03, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.ADD, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x03, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.ADD, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x03, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.ADD, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x02, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, reads_mem=true}},
{{.ADD, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x03, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, reads_mem=true}},
{{.ADD, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x03, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, reads_mem=true}},
{{.ADD, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x03, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, reads_mem=true}},
{{.ADD, {.AL_IMPL, .IMM8, .NONE, .NONE}, {.IMPL, .IB, .NONE, .NONE}, 0x04, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
{{.ADD, {.AX_IMPL, .IMM16, .NONE, .NONE}, {.IMPL, .IW, .NONE, .NONE}, 0x05, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
{{.ADD, {.EAX_IMPL, .IMM32, .NONE, .NONE}, {.IMPL, .ID, .NONE, .NONE}, 0x05, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
@@ -203,10 +203,10 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.ADC, {.RM16, .R16, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x11, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.ADC, {.RM32, .R32, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x11, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.ADC, {.RM64, .R64, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x11, 0, {force_rex_w=true, lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.ADC, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x12, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.ADC, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x13, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.ADC, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x13, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.ADC, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x13, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.ADC, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x12, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, reads_mem=true}},
{{.ADC, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x13, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, reads_mem=true}},
{{.ADC, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x13, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, reads_mem=true}},
{{.ADC, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x13, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, reads_mem=true}},
{{.ADC, {.AL_IMPL, .IMM8, .NONE, .NONE}, {.IMPL, .IB, .NONE, .NONE}, 0x14, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}}},
{{.ADC, {.AX_IMPL, .IMM16, .NONE, .NONE}, {.IMPL, .IW, .NONE, .NONE}, 0x15, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}}},
{{.ADC, {.EAX_IMPL, .IMM32, .NONE, .NONE}, {.IMPL, .ID, .NONE, .NONE}, 0x15, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}}},
@@ -224,10 +224,10 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.SUB, {.RM16, .R16, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x29, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.SUB, {.RM32, .R32, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x29, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.SUB, {.RM64, .R64, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x29, 0, {force_rex_w=true, lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.SUB, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x2A, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.SUB, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x2B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.SUB, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x2B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.SUB, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x2B, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, writes_mem=true, reads_mem=true}},
{{.SUB, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x2A, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, reads_mem=true}},
{{.SUB, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x2B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, reads_mem=true}},
{{.SUB, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x2B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, reads_mem=true}},
{{.SUB, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x2B, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, reads_mem=true}},
{{.SUB, {.AL_IMPL, .IMM8, .NONE, .NONE}, {.IMPL, .IB, .NONE, .NONE}, 0x2C, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
{{.SUB, {.AX_IMPL, .IMM16, .NONE, .NONE}, {.IMPL, .IW, .NONE, .NONE}, 0x2D, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
{{.SUB, {.EAX_IMPL, .IMM32, .NONE, .NONE}, {.IMPL, .ID, .NONE, .NONE}, 0x2D, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
@@ -245,10 +245,10 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.SBB, {.RM16, .R16, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x19, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.SBB, {.RM32, .R32, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x19, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.SBB, {.RM64, .R64, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x19, 0, {force_rex_w=true, lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.SBB, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x1A, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.SBB, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x1B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.SBB, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x1B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.SBB, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x1B, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, writes_mem=true, reads_mem=true}},
{{.SBB, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x1A, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, reads_mem=true}},
{{.SBB, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x1B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, reads_mem=true}},
{{.SBB, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x1B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, reads_mem=true}},
{{.SBB, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x1B, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}, reads_mem=true}},
{{.SBB, {.AL_IMPL, .IMM8, .NONE, .NONE}, {.IMPL, .IB, .NONE, .NONE}, 0x1C, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}}},
{{.SBB, {.AX_IMPL, .IMM16, .NONE, .NONE}, {.IMPL, .IW, .NONE, .NONE}, 0x1D, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}}},
{{.SBB, {.EAX_IMPL, .IMM32, .NONE, .NONE}, {.IMPL, .ID, .NONE, .NONE}, 0x1D, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, flags_rd={.CF}}},
@@ -342,10 +342,10 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.AND, {.RM16, .R16, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x21, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.AND, {.RM32, .R32, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x21, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.AND, {.RM64, .R64, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x21, 0, {force_rex_w=true, lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.AND, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x22, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.AND, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x23, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.AND, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x23, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.AND, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x23, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.AND, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x22, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.AND, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x23, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.AND, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x23, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.AND, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x23, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.AND, {.AL_IMPL, .IMM8, .NONE, .NONE}, {.IMPL, .IB, .NONE, .NONE}, 0x24, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}}},
{{.AND, {.AX_IMPL, .IMM16, .NONE, .NONE}, {.IMPL, .IW, .NONE, .NONE}, 0x25, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}}},
{{.AND, {.EAX_IMPL, .IMM32, .NONE, .NONE}, {.IMPL, .ID, .NONE, .NONE}, 0x25, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}}},
@@ -363,10 +363,10 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.OR, {.RM16, .R16, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x09, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.OR, {.RM32, .R32, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x09, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.OR, {.RM64, .R64, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x09, 0, {force_rex_w=true, lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.OR, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x0A, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.OR, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x0B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.OR, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x0B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.OR, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x0B, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.OR, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x0A, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.OR, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x0B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.OR, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x0B, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.OR, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x0B, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.OR, {.AL_IMPL, .IMM8, .NONE, .NONE}, {.IMPL, .IB, .NONE, .NONE}, 0x0C, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}}},
{{.OR, {.AX_IMPL, .IMM16, .NONE, .NONE}, {.IMPL, .IW, .NONE, .NONE}, 0x0D, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}}},
{{.OR, {.EAX_IMPL, .IMM32, .NONE, .NONE}, {.IMPL, .ID, .NONE, .NONE}, 0x0D, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}}},
@@ -384,10 +384,10 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.XOR, {.RM16, .R16, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x31, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.XOR, {.RM32, .R32, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x31, 0, {lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.XOR, {.RM64, .R64, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x31, 0, {force_rex_w=true, lock_ok=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.XOR, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x32, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.XOR, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x33, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.XOR, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x33, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.XOR, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x33, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.XOR, {.R8, .RM8, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x32, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.XOR, {.R16, .RM16, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x33, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.XOR, {.R32, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x33, 0, {}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.XOR, {.R64, .RM64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x33, 0, {force_rex_w=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, reads_mem=true}},
{{.XOR, {.AL_IMPL, .IMM8, .NONE, .NONE}, {.IMPL, .IB, .NONE, .NONE}, 0x34, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}}},
{{.XOR, {.AX_IMPL, .IMM16, .NONE, .NONE}, {.IMPL, .IW, .NONE, .NONE}, 0x35, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}}},
{{.XOR, {.EAX_IMPL, .IMM32, .NONE, .NONE}, {.IMPL, .ID, .NONE, .NONE}, 0x35, 0, {}}, {read={1}, implicit_wr={.RAX}, implicit_rd={.RAX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}}},
@@ -462,6 +462,20 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.SAR, {.RM64, .CL_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD3, 7, {force_rex_w=true, modrm_reg_ext=true}}, {written={0}, read={0}, implicit_rd={.RCX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAR, {.RM64, .IMM8, .NONE, .NONE}, {.MR, .IB, .NONE, .NONE}, 0xC1, 7, {force_rex_w=true, modrm_reg_ext=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
},
.SAL = {
{{.SAL, {.RM8, .ONE_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD0, 4, {modrm_reg_ext=true}}, {written={0}, read={0}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM8, .CL_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD2, 4, {modrm_reg_ext=true}}, {written={0}, read={0}, implicit_rd={.RCX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM8, .IMM8, .NONE, .NONE}, {.MR, .IB, .NONE, .NONE}, 0xC0, 4, {modrm_reg_ext=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM16, .ONE_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD1, 4, {modrm_reg_ext=true}}, {written={0}, read={0}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM16, .CL_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD3, 4, {modrm_reg_ext=true}}, {written={0}, read={0}, implicit_rd={.RCX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM16, .IMM8, .NONE, .NONE}, {.MR, .IB, .NONE, .NONE}, 0xC1, 4, {modrm_reg_ext=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM32, .ONE_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD1, 4, {modrm_reg_ext=true}}, {written={0}, read={0}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM32, .CL_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD3, 4, {modrm_reg_ext=true}}, {written={0}, read={0}, implicit_rd={.RCX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM32, .IMM8, .NONE, .NONE}, {.MR, .IB, .NONE, .NONE}, 0xC1, 4, {modrm_reg_ext=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM64, .ONE_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD1, 4, {force_rex_w=true, modrm_reg_ext=true}}, {written={0}, read={0}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM64, .CL_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD3, 4, {force_rex_w=true, modrm_reg_ext=true}}, {written={0}, read={0}, implicit_rd={.RCX}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
{{.SAL, {.RM64, .IMM8, .NONE, .NONE}, {.MR, .IB, .NONE, .NONE}, 0xC1, 4, {force_rex_w=true, modrm_reg_ext=true}}, {written={0}, read={0, 1}, flags_wr={.CF, .PF, .ZF, .SF, .OF}, flags_undef={.AF}, writes_mem=true, reads_mem=true}},
},
.ROL = {
{{.ROL, {.RM8, .ONE_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD0, 0, {modrm_reg_ext=true}}, {written={0}, read={0}, flags_wr={.CF, .OF}, writes_mem=true, reads_mem=true}},
{{.ROL, {.RM8, .CL_IMPL, .NONE, .NONE}, {.MR, .IMPL, .NONE, .NONE}, 0xD2, 0, {modrm_reg_ext=true}}, {written={0}, read={0}, implicit_rd={.RCX}, flags_wr={.CF, .OF}, writes_mem=true, reads_mem=true}},
@@ -1166,19 +1180,19 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.UD2, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x0B, 0, {esc=._0F}}, {side_effects={.TRAP}}},
},
.CPUID = {
{{.CPUID, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0xA2, 0, {esc=._0F}}, {implicit_wr={.RAX, .RBX, .RCX, .RDX}, implicit_rd={.RAX, .RCX}, side_effects={.SERIALIZING}}},
{{.CPUID, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0xA2, 0, {esc=._0F}}, {implicit_wr={.RAX, .RBX, .RCX, .RDX}, implicit_rd={.RAX, .RCX}, side_effects={.SERIALIZING, .NONDETERMINISTIC}}},
},
.RDTSC = {
{{.RDTSC, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x31, 0, {esc=._0F}}, {implicit_wr={.RAX, .RDX}}},
{{.RDTSC, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x31, 0, {esc=._0F}}, {implicit_wr={.RAX, .RDX}, side_effects={.NONDETERMINISTIC}}},
},
.RDTSCP = {
{{.RDTSCP, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x01, 0xF9, {esc=._0F}}, {implicit_wr={.RAX, .RCX, .RDX}}},
{{.RDTSCP, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x01, 0xF9, {esc=._0F}}, {implicit_wr={.RAX, .RCX, .RDX}, side_effects={.NONDETERMINISTIC}}},
},
.RDPMC = {
{{.RDPMC, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x33, 0, {esc=._0F}}, {implicit_wr={.RAX, .RDX}, implicit_rd={.RCX}}},
{{.RDPMC, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x33, 0, {esc=._0F}}, {implicit_wr={.RAX, .RDX}, implicit_rd={.RCX}, side_effects={.NONDETERMINISTIC}}},
},
.XGETBV = {
{{.XGETBV, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x01, 0xD0, {esc=._0F}}, {implicit_wr={.RAX, .RDX}, implicit_rd={.RCX}}},
{{.XGETBV, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x01, 0xD0, {esc=._0F}}, {implicit_wr={.RAX, .RDX}, implicit_rd={.RCX}, side_effects={.NONDETERMINISTIC}}},
},
.XSETBV = {
{{.XSETBV, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x01, 0xD1, {esc=._0F}}, {implicit_rd={.RAX, .RCX, .RDX}}},
@@ -1298,9 +1312,9 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.MOVQ, {.XMM, .R64, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x6E, 0, {esc=._0F, prefix=PREFIX_66, force_rex_w=true}}, {written={0}, read={1}}},
},
.MOVD = {
{{.MOVD, {.XMM, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x6E, 0, {esc=._0F, prefix=PREFIX_66}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOVD, {.XMM, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x6E, 0, {esc=._0F, prefix=PREFIX_66}}, {written={0}, read={1}, reads_mem=true}},
{{.MOVD, {.RM32, .XMM, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x7E, 0, {esc=._0F, prefix=PREFIX_66}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOVD, {.MM, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x6E, 0, {esc=._0F}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.MOVD, {.MM, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x6E, 0, {esc=._0F}}, {written={0}, read={1}, reads_mem=true}},
{{.MOVD, {.RM32, .MM, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x7E, 0, {esc=._0F}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
},
.MOVLPS = {
@@ -2352,7 +2366,7 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.VMOVQ, {.R64, .XMM, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x7E, 0, {esc=._0F, prefix=PREFIX_66, vex_type=.VEX, vex_w=.W1, vex_l=.L0}}, {written={0}, read={1}}},
},
.VMOVD = {
{{.VMOVD, {.XMM, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x6E, 0, {esc=._0F, prefix=PREFIX_66, vex_type=.VEX, vex_l=.L0}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
{{.VMOVD, {.XMM, .RM32, .NONE, .NONE}, {.REG, .MR, .NONE, .NONE}, 0x6E, 0, {esc=._0F, prefix=PREFIX_66, vex_type=.VEX, vex_l=.L0}}, {written={0}, read={1}, reads_mem=true}},
{{.VMOVD, {.RM32, .XMM, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0x7E, 0, {esc=._0F, prefix=PREFIX_66, vex_type=.VEX, vex_l=.L0}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
},
.VMOVLPS = {
@@ -4647,7 +4661,7 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.RSM, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0xAA, 0, {esc=._0F}}, {side_effects={.SERIALIZING, .PRIVILEGED}}},
},
.RDMSR = {
{{.RDMSR, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x32, 0, {esc=._0F}}, {implicit_wr={.RAX, .RDX}, implicit_rd={.RCX}, side_effects={.PRIVILEGED}}},
{{.RDMSR, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x32, 0, {esc=._0F}}, {implicit_wr={.RAX, .RDX}, implicit_rd={.RCX}, side_effects={.PRIVILEGED, .NONDETERMINISTIC}}},
},
.WRMSR = {
{{.WRMSR, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x30, 0, {esc=._0F}}, {implicit_rd={.RAX, .RCX, .RDX}, side_effects={.SERIALIZING, .PRIVILEGED}}},
@@ -4855,14 +4869,14 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.MOVBE, {.M64, .R64, .NONE, .NONE}, {.MR, .REG, .NONE, .NONE}, 0xF1, 0, {esc=._0F38, force_rex_w=true}}, {written={0}, read={1}, writes_mem=true, reads_mem=true}},
},
.RDRAND = {
{{.RDRAND, {.R16, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 6, {esc=._0F, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
{{.RDRAND, {.R32, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 6, {esc=._0F, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
{{.RDRAND, {.R64, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 6, {esc=._0F, force_rex_w=true, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
{{.RDRAND, {.R16, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 6, {esc=._0F, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, side_effects={.NONDETERMINISTIC}}},
{{.RDRAND, {.R32, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 6, {esc=._0F, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, side_effects={.NONDETERMINISTIC}}},
{{.RDRAND, {.R64, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 6, {esc=._0F, force_rex_w=true, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, side_effects={.NONDETERMINISTIC}}},
},
.RDSEED = {
{{.RDSEED, {.R16, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 7, {esc=._0F, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
{{.RDSEED, {.R32, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 7, {esc=._0F, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
{{.RDSEED, {.R64, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 7, {esc=._0F, force_rex_w=true, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}}},
{{.RDSEED, {.R16, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 7, {esc=._0F, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, side_effects={.NONDETERMINISTIC}}},
{{.RDSEED, {.R32, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 7, {esc=._0F, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, side_effects={.NONDETERMINISTIC}}},
{{.RDSEED, {.R64, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 7, {esc=._0F, force_rex_w=true, modrm_reg_ext=true}}, {written={0}, flags_wr={.CF, .PF, .AF, .ZF, .SF, .OF}, side_effects={.NONDETERMINISTIC}}},
},
.SWAPGS = {
{{.SWAPGS, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x01, 0xF8, {esc=._0F}}, {side_effects={.PRIVILEGED}}},
@@ -4900,7 +4914,7 @@ INSTRUCTION_TABLE := [Mnemonic][]Form{
{{.PTWRITE, {.RM64, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xAE, 4, {esc=._0F, prefix=PREFIX_F3, force_rex_w=true, modrm_reg_ext=true}}, {read={0}, reads_mem=true}},
},
.RDPID = {
{{.RDPID, {.R64, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 7, {esc=._0F, prefix=PREFIX_F3, modrm_reg_ext=true}}, {written={0}}},
{{.RDPID, {.R64, .NONE, .NONE, .NONE}, {.MR, .NONE, .NONE, .NONE}, 0xC7, 7, {esc=._0F, prefix=PREFIX_F3, modrm_reg_ext=true}}, {written={0}, side_effects={.NONDETERMINISTIC}}},
},
.WBNOINVD = {
{{.WBNOINVD, {.NONE, .NONE, .NONE, .NONE}, {.NONE, .NONE, .NONE, .NONE}, 0x09, 0, {esc=._0F, prefix=PREFIX_F3}}, {side_effects={.SERIALIZING, .PRIVILEGED}}},
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.
Binary file not shown.
+13
View File
@@ -7,6 +7,19 @@ foreign import ntdll_lib "system:ntdll.lib"
foreign ntdll_lib {
RtlGetVersion :: proc(lpVersionInformation: ^OSVERSIONINFOEXW) -> NTSTATUS ---
NtDeviceIoControlFile :: proc(
FileHandle: HANDLE,
Event: HANDLE,
ApcRoutine: PIO_APC_ROUTINE,
ApcContext: rawptr,
IoStatusBlock: PIO_STATUS_BLOCK,
IoControlCode: ULONG,
InputBuffer: rawptr,
InputBufferLength: ULONG,
OutputBuffer: rawptr,
OutputBufferLength: ULONG,
) -> NTSTATUS ---
NtQueryInformationProcess :: proc(
ProcessHandle: HANDLE,
+14
View File
@@ -53,6 +53,20 @@ gb_global String const asm_operand_kind_expected_strings[AsmOperand_COUNT] = {
str_lit("a label"),
};
enum AsmOperandConstraintKind : u32 {
AsmOperandConstraint_None = 0,
AsmOperandConstraint_ShiftCount, // integer const must satisfy 0 <= v < width
AsmOperandConstraint_NonZeroDivisor, // integer const must be != 0
AsmOperandConstraint_COUNT
};
struct AsmOperandConstraint {
AsmOperandConstraintKind kind;
i32 width_operand; // ShiftCount
};
#include "asm_tables_amd64.cpp"
#include "asm_tables_riscv.cpp"
+892 -801
View File
File diff suppressed because it is too large. Load diff
+76 -10
View File
@@ -68,12 +68,12 @@ struct Asm_riscv {
};
enum ClobberFFlags : u8 {
ClobberFFlag_NV = 1<<0, // invalid operation
ClobberFFlag_DZ = 1<<1, // divide by zero
ClobberFFlag_OF = 1<<2, // overflow
ClobberFFlag_UF = 1<<3, // underflow
ClobberFFlag_NX = 1<<4, // inexact
enum ClobberFlags : u8 {
ClobberFlag_NV = 1<<0, // invalid operation
ClobberFlag_DZ = 1<<1, // divide by zero
ClobberFlag_OF = 1<<2, // overflow
ClobberFlag_UF = 1<<3, // underflow
ClobberFlag_NX = 1<<4, // inexact
};
enum ClobberRegs : u8 {
@@ -122,9 +122,28 @@ struct Asm_riscv {
return "<reg>";
}
u16 flags_from_name(String const &name) {
static const struct { String name; ClobberFlags flag; } table[] = {
// flags: accrued FP exception flags (fcsr[4:0])
{str_lit("nx"), ClobberFlag_NX}, // Inexact
{str_lit("uf"), ClobberFlag_UF}, // Underflow
{str_lit("of"), ClobberFlag_OF}, // Overflow
{str_lit("dz"), ClobberFlag_DZ}, // Divide by Zero
{str_lit("nv"), ClobberFlag_NV}, // Invalid Operation
};
for (auto const &t : table) {
if (name == t.name) {
return cast(u16)t.flag;
}
}
return 0;
}
i32 flag_bit_from_name(String const &name, i32 *width_) {
static const struct { String name; i32 bit; } table[] = {
// fflags: accrued FP exception flags (fcsr[4:0])
// flags: accrued FP exception flags (fcsr[4:0])
{str_lit("nx"), 0}, // Inexact
{str_lit("uf"), 1}, // Underflow
{str_lit("of"), 2}, // Overflow
@@ -155,14 +174,14 @@ struct Asm_riscv {
OperandSet read; // operand slots whose register/CSR/mem-base is read
ClobberRegs implicit_wr; // implicit reg writes (ra on C.JAL/C.JALR)
ClobberRegs implicit_rd; // implicit reg reads (sp on the *SP forms)
ClobberFFlags fflags_wr; // accrued exception flags this op may raise
ClobberFlags flags_wr; // accrued exception flags this op may raise
bool reads_frm; // consumes the dynamic rounding mode from fcsr
bool writes_mem;
bool reads_mem;
SideEffectFlags side_effects;
bool implies_clobber_flags() const {
return (fflags_wr != 0);
return (flags_wr != 0);
}
bool implies_clobber_memory() const {
return writes_mem || reads_mem ||
@@ -184,6 +203,17 @@ struct Asm_riscv {
bool is_conditional() const {
return has_control();
}
bool is_nondeterministic() const {
return false;
}
bool has_implicit_mem() const {
if (!writes_mem && !reads_mem) {
return false;
}
u16 implicit = cast(u16)implicit_rd | cast(u16)implicit_wr;
bool is_atomic = false; // TODO(bill): Add ATOMIC flag to SideEffectFlags in the original INSTRUCTION_TABLE
return (implicit & (ClobberReg_SP)) != 0 || is_atomic;
}
};
void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) {
@@ -216,6 +246,17 @@ struct Asm_riscv {
u16 csr; // CSR address when a src slot is AliasSrc_CSR_LIT
u8 nargs; // operands the user supplies (ARG0..<ARGn)
bool rv32_only; // base gate (the *h counter reads)
// Nondeterministic iff this is a CSR access whose CSR operand names a counter/timer/entropy register (extension-gated; absent CSRs never match)
bool is_nondeterministic() const {
if (csr == 0x015) return true; // seed (Zkr)
if (0xC00 <= csr && csr <= 0xC1F) return true; // cycle/time/instret + hpm (unpriv)
if (0xC80 <= csr && csr <= 0xC9F) return true; // rv32 high halves (unpriv)
if (0xB00 <= csr && csr <= 0xB1F) return true; // mcycle/minstret + mhpm
if (0xB80 <= csr && csr <= 0xB9F) return true; // rv32 high halves (machine)
return false;
}
};
enum PseudoMnemonic : u16 {
@@ -244,7 +285,10 @@ struct Asm_riscv {
PseudoAlias pseudo_alias(u16 pm) {
PseudoAlias *pa = (PseudoAlias *)raw_pseudo_aliases;
return pa[pm];
} static String const pseudo_mnemonic_strings[PSEUDO_MNEMONIC_COUNT];
}
static String const pseudo_mnemonic_strings[PSEUDO_MNEMONIC_COUNT];
static u16 const register_codes [REG_COUNT];
static String const register_strings[REG_COUNT];
@@ -720,6 +764,28 @@ struct Asm_riscv {
// RISC-V does not have prefixes
return false;
}
AsmOperandConstraint operand_value_constraint(u16 m, int op) const {
switch (m) {
case M_SLLI: case M_SRLI: case M_SRAI:
if (op == 2) return {AsmOperandConstraint_ShiftCount, /*XLEN*/-1};
break;
case M_DIV: case M_DIVU: case M_REM: case M_REMU:
if (op == 2) return {AsmOperandConstraint_NonZeroDivisor, -1};
break;
}
return {AsmOperandConstraint_None, -1};
} bool is_self_zeroing_idiom(u16 m) const {
switch (m) {
case M_XOR:
case M_SUB:
case M_SUBW:
case M_SLT:
case M_SLTU:
case M_ANDN:
return true;
}
return false;
}
};
+26 -17
View File
@@ -464,6 +464,15 @@ enum IntegerDivisionByZeroKind : u8 {
IntegerDivisionByZero_AllBits,
};
// values of BuildContext.optimization_level;
// matches Odin_Optimization_Mode in checker.cpp
enum OptimizationLevel : i32 {
OptimizationLevel_None = -1,
OptimizationLevel_Minimal = 0,
OptimizationLevel_Size = 1,
OptimizationLevel_Speed = 2,
OptimizationLevel_Aggressive = 3,
};
// This stores the information for the specify architecture of this build
struct BuildContext {
@@ -2137,9 +2146,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
gb_internal bool check_single_target_feature_is_valid(String const &feature_list, String const &feature) {
String_Iterator it = {feature_list, 0};
for (;;) {
String str = string_split_iterator(&it, ',');
if (str == "") break;
String str = {};
while (string_split_iterator_next(&it, ',', &str)) {
if (str == feature) {
return true;
}
@@ -2151,8 +2159,8 @@ gb_internal bool check_single_target_feature_is_valid(String const &feature_list
gb_internal bool check_target_feature_is_valid(String const &feature, TargetArchKind arch, String *invalid) {
String feature_list = target_features_list[arch];
String_Iterator it = {feature, 0};
for (;;) {
String str = string_split_iterator(&it, ',');
String str = {};
while (string_split_iterator_next(&it, ',', &str)) {
String feature_str = str;
if (string_starts_with(feature_str, '+') || string_starts_with(feature_str, '-')) {
feature_str = substring(feature_str, 1, feature_str.len);
@@ -2160,7 +2168,6 @@ gb_internal bool check_target_feature_is_valid(String const &feature, TargetArch
return false;
}
}
if (feature_str == "") break;
if (!check_single_target_feature_is_valid(feature_list, feature_str)) {
if (invalid) *invalid = str;
return false;
@@ -2172,10 +2179,8 @@ gb_internal bool check_target_feature_is_valid(String const &feature, TargetArch
gb_internal bool check_target_feature_is_valid_globally(String const &feature, String *invalid) {
String_Iterator it = {feature, 0};
for (;;) {
String str = string_split_iterator(&it, ',');
if (str == "") break;
String str = {};
while (string_split_iterator_next(&it, ',', &str)) {
bool valid = false;
for (int arch = TargetArch_Invalid; arch < TargetArch_COUNT; arch += 1) {
if (check_target_feature_is_valid(str, cast(TargetArchKind)arch, invalid)) {
@@ -2199,15 +2204,19 @@ gb_internal bool check_target_feature_is_valid_for_target_arch(String const &fea
gb_internal bool check_target_feature_is_enabled(String const &feature, String *not_enabled) {
String_Iterator it = {feature, 0};
for (;;) {
String str = string_split_iterator(&it, ',');
String str = {};
while (string_split_iterator_next(&it, ',', &str)) {
String feature_str = str;
bool want_enabled = true;
if (string_starts_with(feature_str, '+') || string_starts_with(feature_str, '-')) {
want_enabled = feature_str[0] == '+';
feature_str = substring(feature_str, 1, feature_str.len);
}
if (feature_str == "") break;
if (feature_str == "") {
// a bare sign names no feature, which cannot be enabled
if (not_enabled) *not_enabled = str;
return false;
}
String plus_str = concatenate_strings(temporary_allocator(), make_string_c("+"), feature_str);
String minus_str = concatenate_strings(temporary_allocator(), make_string_c("-"), feature_str);
@@ -2231,9 +2240,8 @@ gb_internal bool check_target_feature_is_enabled(String const &feature, String *
gb_internal bool check_target_feature_is_superset_of(String const &superset, String const &of, String *missing) {
String_Iterator it = {of, 0};
for (;;) {
String str = string_split_iterator(&it, ',');
if (str == "") break;
String str = {};
while (string_split_iterator_next(&it, ',', &str)) {
if (!check_single_target_feature_is_valid(superset, str)) {
if (missing) *missing = str;
return false;
@@ -2630,12 +2638,13 @@ gb_internal bool init_build_paths(String init_filename) {
if (build_context.no_crt && !build_context.no_thread_local) {
switch (build_context.metrics.os) {
case TargetOs_windows:
case TargetOs_linux:
case TargetOs_darwin:
case TargetOs_freebsd:
case TargetOs_openbsd:
case TargetOs_netbsd:
gb_printf_err("-no-crt on Unix systems requires the -no-thread-local flag to also be present, because the TLS is inaccessible without CRT\n");
gb_printf_err("-no-crt requires the -no-thread-local flag to also be present, because the TLS is inaccessible without CRT\n");
no_crt_checks_failed = true;
}
}
+579 -374
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+4
View File
@@ -13906,6 +13906,10 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
str = gb_string_appendc(str, " = ");
str = write_expr_to_string(str, spec->value, shorthand);
}
for (Ast *dir : spec->directives) {
str = gb_string_appendc(str, " ");
str = write_expr_to_string(str, dir, shorthand);
}
case_end;
case_ast_node(clobber, AsmClobber, node);
+6
View File
@@ -196,6 +196,8 @@ struct AsmTemplateEntityDecl {
i32 view_of; // total_index of the source operand this is a width-view of, else -1
i32 view_bits; // the view width in bits, otherwise 0
bool no_init;
};
// An Entity is a named "thing" in the language
@@ -340,11 +342,14 @@ struct Entity {
String name;
Ast *node;
Ast *parent;
i32 asm_block_index;
} Label;
struct {
Ast *node;
bool is_volatile;
bool is_align_stack;
bool is_pure;
bool has_observable_side_effect;
@@ -574,6 +579,7 @@ gb_internal Entity *alloc_entity_label(Scope *scope, Token token, Type *type, As
Entity *entity = alloc_entity(Entity_Label, scope, token, type);
entity->Label.node = node;
entity->Label.parent = parent;
entity->Label.asm_block_index = -1;
entity->state = EntityState_Resolved;
return entity;
}
+3 -3
View File
@@ -2438,6 +2438,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
lbFunctionPassManagerKind pass_manager_kind = lbFunctionPassManager_default;
if (p->flags & lbProcedureFlag_WithoutMemcpyPass) {
pass_manager_kind = lbFunctionPassManager_default_without_memcpy;
lb_remove_attribute_from_proc(p->module, p->value, "optsize"); // incompatible with optnone
lb_add_attribute_to_proc(p->module, p->value, "optnone");
lb_add_attribute_to_proc(p->module, p->value, "noinline");
} else {
@@ -3163,10 +3164,9 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
gbString llvm_features = gb_string_make(temporary_allocator(), "");
String_Iterator it = {build_context.target_features_string, 0};
String str = {};
bool first = true;
for (;;) {
String str = string_split_iterator(&it, ',');
if (str == "") break;
while (string_split_iterator_next(&it, ',', &str)) {
if (!first) {
llvm_features = gb_string_appendc(llvm_features, ",");
}
+36 -24
View File
@@ -322,6 +322,21 @@ gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_beha
}
// LLVM has srem(min(Integer_Type), -1) as UB and it raises an FP exception on a hardware
// divide, yet `x % -1` is 0 for every x; `x srem 1` is 0 too and cannot trap, so a runtime
// -1 divisor can be swapped for 1. Vectorizable.
gb_internal LLVMValueRef lb_srem_safe_divisor(lbProcedure *p, LLVMValueRef rhs) {
LLVMValueRef minus_one = LLVMConstAllOnes(LLVMTypeOf(rhs));
// build 1 as neg(-1), this folds for both scalars and vectors
LLVMValueRef one = LLVMBuildNeg(p->builder, minus_one, "");
if (LLVMIsAConstantInt(rhs)) {
return rhs == minus_one ? one : rhs;
}
LLVMValueRef is_minus_one = LLVMBuildICmp(p->builder, LLVMIntEQ, rhs, minus_one, "");
return LLVMBuildSelect(p->builder, is_minus_one, one, rhs, "");
}
// implements %% (the remainder/floored mod operator) on signed integers;
// this is branchless and vectorizable, so it also covers vectors
gb_internal LLVMValueRef lb_emit_signed_floor_mod(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs) {
@@ -329,24 +344,11 @@ gb_internal LLVMValueRef lb_emit_signed_floor_mod(lbProcedure *p, LLVMValueRef l
// and works for arbitrary precision integers, but the add can wrap at finite precision
// the Odin spec mandates min(Integer_Type) %% -1 must be 0,
// but LLVM has srem(min(Integer_Type), -1) as UB and results in FP exception;
// since x %% -1 == 0 for every x, a constant rhs = -1 can fold,
// and a runtime -1 can be swapped with 1 (x srem 1 is 0 for every x, no exceptions)
LLVMValueRef minus_one = LLVMConstAllOnes(LLVMTypeOf(rhs));
LLVMValueRef safe_rhs = rhs;
if (LLVMIsAConstantInt(rhs)) {
if (rhs == minus_one) {
return LLVMConstNull(LLVMTypeOf(rhs)); // the entire %% op folds to 0
}
} else {
// safe_rhs = (rhs == -1) ? 1 : rhs
// vectorizable construction,
// build 1 as neg(-1), this folds for both scalars and vectors
LLVMValueRef one = LLVMBuildNeg(p->builder, minus_one, "");
LLVMValueRef is_minus_one = LLVMBuildICmp(p->builder, LLVMIntEQ, rhs, minus_one, "");
safe_rhs = LLVMBuildSelect(p->builder, is_minus_one, one, rhs, "");
// a constant rhs = -1 can fold the whole operation, a runtime one is handled by the swap
if (LLVMIsAConstantInt(rhs) && rhs == LLVMConstAllOnes(LLVMTypeOf(rhs))) {
return LLVMConstNull(LLVMTypeOf(rhs)); // the entire %% op folds to 0
}
LLVMValueRef r = LLVMBuildSRem(p->builder, lhs, safe_rhs, "");
LLVMValueRef r = LLVMBuildSRem(p->builder, lhs, lb_srem_safe_divisor(p, rhs), "");
// srem truncs to 0, so r needs a +rhs correction when the operands signs differ (and r != 0)
// so we implement
// r = lhs % rhs
@@ -498,9 +500,10 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu
}
break;
case Token_Mod:
{
auto *call = is_type_unsigned(integral_type) ? LLVMBuildURem : LLVMBuildSRem;
z = call(p->builder, x, y, "");
if (is_type_unsigned(integral_type)) {
z = LLVMBuildURem(p->builder, x, y, "");
} else {
z = LLVMBuildSRem(p->builder, x, lb_srem_safe_divisor(p, y), "");
}
break;
case Token_ModMod:
@@ -610,9 +613,10 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu
}
break;
case Token_Mod:
{
auto *call = is_type_unsigned(integral_type) ? LLVMBuildURem : LLVMBuildSRem;
z = call(p->builder, x, y, "");
if (is_type_unsigned(integral_type)) {
z = LLVMBuildURem(p->builder, x, y, "");
} else {
z = LLVMBuildSRem(p->builder, x, lb_srem_safe_divisor(p, y), "");
}
break;
case Token_ModMod:
@@ -1684,7 +1688,8 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV
if (is_unsigned) {
return LLVMBuildURem(p->builder, lhs, rhs, "");
} else {
return LLVMBuildSRem(p->builder, lhs, rhs, "");
// min(Integer_Type) % -1 is 0, matching the constant folder, and must not trap
return LLVMBuildSRem(p->builder, lhs, lb_srem_safe_divisor(p, rhs), "");
}
}
};
@@ -2407,6 +2412,13 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
// boolean -> boolean/integer
if (is_type_boolean(src) && (is_type_boolean(dst) || is_type_integer(dst))) {
LLVMValueRef b = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(lb_type(m, value.type)), "");
if (type_size_of(default_type(dst)) > 1 && is_type_different_to_arch_endianness(dst)) {
Type *platform_dst_type = integer_endian_type_to_platform_type(dst);
lbValue res = {};
res.value = LLVMBuildIntCast2(p->builder, b, lb_type(m, platform_dst_type), false, "");
res.type = t;
return lb_emit_byte_swap(p, res, t);
}
lbValue res = {};
res.value = LLVMBuildIntCast2(p->builder, b, lb_type(m, t), false, "");
res.type = t;
+4
View File
@@ -3033,6 +3033,10 @@ gb_internal void lb_add_attribute_to_proc(lbModule *m, LLVMValueRef proc_value,
LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(m->ctx, name, value));
}
gb_internal void lb_remove_attribute_from_proc(lbModule *m, LLVMValueRef proc_value, char const *name) {
LLVMRemoveEnumAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, LLVMGetEnumAttributeKindForName(name, gb_strlen(name)));
}
gb_internal bool lb_proc_has_attribute(lbModule *m, LLVMValueRef proc_value, char const *name) {
LLVMAttributeRef ref = LLVMGetEnumAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, LLVMGetEnumAttributeKindForName(name, gb_strlen(name)));
return ref != nullptr;
+9 -3
View File
@@ -213,16 +213,22 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
case ProcedureOptimizationMode_FavorSize:
lb_add_attribute_to_proc(m, p->value, "optsize");
break;
default:
// need optsize per proc for -o:size;
// (inliner, unroller, vectorizer, etc check it)
if (build_context.optimization_level == OptimizationLevel_Size) {
lb_add_attribute_to_proc(m, p->value, "optsize");
}
break;
}
if (pt->Proc.enable_target_feature.len != 0) {
gbString feature_str = gb_string_make(temporary_allocator(), "");
String_Iterator it = {pt->Proc.enable_target_feature, 0};
String str = {};
bool first = true;
for (;;) {
String str = string_split_iterator(&it, ',');
if (str == "") break;
while (string_split_iterator_next(&it, ',', &str)) {
bool add_prefix = !(string_starts_with(str, '+') || string_starts_with(str, '-'));
if (!first) {
feature_str = gb_string_appendc(feature_str, ",");
+14 -28
View File
@@ -1455,12 +1455,8 @@ gb_internal bool parse_build_flags(Array<String> args) {
GB_ASSERT(value.kind == ExactValue_String);
String val = value.value_string;
String_Iterator it = {val, 0};
for (;;) {
String pkg = string_split_iterator(&it, ',');
if (pkg.len == 0) {
break;
}
String pkg = {};
while (string_split_iterator_next(&it, ',', &pkg)) {
pkg = string_trim_whitespace(pkg);
if (!string_is_valid_identifier(pkg)) {
gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(pkg));
@@ -1478,12 +1474,8 @@ gb_internal bool parse_build_flags(Array<String> args) {
GB_ASSERT(value.kind == ExactValue_String);
String val = value.value_string;
String_Iterator it = {val, 0};
for (;;) {
String attr = string_split_iterator(&it, ',');
if (attr.len == 0) {
break;
}
String attr = {};
while (string_split_iterator_next(&it, ',', &attr)) {
attr = string_trim_whitespace(attr);
if (!string_is_valid_identifier(attr)) {
gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(attr));
@@ -4166,9 +4158,8 @@ int main(int arg_count, char const **arg_ptr) {
} else {
String march_list = target_microarch_list[build_context.metrics.arch];
String_Iterator it = {march_list, 0};
for (;;) {
String str = string_split_iterator(&it, ',');
if (str == "") break;
String str = {};
while (string_split_iterator_next(&it, ',', &str)) {
if (str == build_context.microarch) {
// Found matching microarch
print_microarch_list = false;
@@ -4193,9 +4184,8 @@ int main(int arg_count, char const **arg_ptr) {
String march_list = target_microarch_list[build_context.metrics.arch];
String_Iterator it = {march_list, 0};
for (;;) {
String str = string_split_iterator(&it, ',');
if (str == "") break;
String str = {};
while (string_split_iterator_next(&it, ',', &str)) {
if (str == default_march) {
gb_printf("\t%.*s (default)\n", LIT(str));
} else {
@@ -4209,9 +4199,8 @@ int main(int arg_count, char const **arg_ptr) {
String default_features = get_default_features();
{
String_Iterator it = {default_features, 0};
for (;;) {
String str = string_split_iterator(&it, ',');
if (str == "") break;
String str = {};
while (string_split_iterator_next(&it, ',', &str)) {
string_set_add(&build_context.target_features_set, str);
}
}
@@ -4231,10 +4220,8 @@ int main(int arg_count, char const **arg_ptr) {
if (build_context.target_features_string.len != 0) {
String_Iterator target_it = {build_context.target_features_string, 0};
for (;;) {
String item = string_split_iterator(&target_it, ',');
if (item == "") break;
String item = {};
while (string_split_iterator_next(&target_it, ',', &item)) {
String stripped_item = item;
if (*stripped_item.text == '+' || *stripped_item.text == '-') {
stripped_item.text++;
@@ -4251,9 +4238,8 @@ int main(int arg_count, char const **arg_ptr) {
String feature_list = target_features_list[build_context.metrics.arch];
String_Iterator it = {feature_list, 0};
for (;;) {
String str = string_split_iterator(&it, ',');
if (str == "") break;
String str = {};
while (string_split_iterator_next(&it, ',', &str)) {
if (check_single_target_feature_is_valid(default_features, str)) {
if (has_ansi_terminal_colours()) {
gb_printf("\t%.*s\x1b[38;5;244m (implied by target microarch %.*s)\x1b[0m\n", LIT(str), LIT(march));
+11
View File
@@ -674,6 +674,17 @@ gb_internal void write_canonical_entity_name(TypeWriter *w, Entity *e) {
write_scope_index_suffix = true;
}
goto write_base_name;
} else if (s->decl_info != nullptr && s->decl_info->proc_lit != nullptr) {
Ast *proc_lit = s->decl_info->proc_lit;
String file_name = filename_without_directory(proc_lit->file()->fullpath);
type_writer_append(w, e->pkg->name.text, e->pkg->name.len);
type_writer_append_fmt(w, CANONICAL_NAME_SEPARATOR CANONICAL_ANON_PREFIX "_%.*s:%d" CANONICAL_NAME_SEPARATOR,
LIT(file_name), ast_token(proc_lit).pos.offset);
if (e->scope->index > 0) {
write_scope_index_suffix = true;
}
goto write_base_name;
} else if ((s->flags & ScopeFlag_File) && s->file != nullptr) {
String file_name = filename_without_directory(s->file->fullpath);
+63 -40
View File
@@ -533,10 +533,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
case Ast_AsmRegister:
break;
case Ast_AsmSpec:
n->AsmSpec.name = clone_ast(n->AsmSpec.name, f);
n->AsmSpec.tied_name = clone_ast(n->AsmSpec.tied_name, f);
n->AsmSpec.type = clone_ast(n->AsmSpec.type, f);
n->AsmSpec.value = clone_ast(n->AsmSpec.value, f);
n->AsmSpec.name = clone_ast(n->AsmSpec.name, f);
n->AsmSpec.tied_name = clone_ast(n->AsmSpec.tied_name, f);
n->AsmSpec.type = clone_ast(n->AsmSpec.type, f);
n->AsmSpec.value = clone_ast(n->AsmSpec.value, f);
n->AsmSpec.directives = clone_ast_array(n->AsmSpec.directives, f);
break;
case Ast_AsmClobber:
n->AsmClobber.value = clone_ast(n->AsmClobber.value, f);
@@ -547,6 +548,7 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
case Ast_AsmInstruction:
n->AsmInstruction.name = clone_ast(n->AsmInstruction.name, f);
n->AsmInstruction.operands = clone_ast_array(n->AsmInstruction.operands, f);
n->AsmInstruction.facts = nullptr;
break;
case Ast_AsmMemoryOperand:
n->AsmMemoryOperand.segment_override = clone_ast(n->AsmMemoryOperand.segment_override, f);
@@ -2694,6 +2696,60 @@ gb_internal Ast *parse_asm_signature(AstFile *f, Token asm_token) {
return ast_proc_type(f, asm_token, params, results, tags, cc, is_generic, diverging);
}
gb_internal Ast *parse_asm_spec(AstFile *f) {
Ast *name = parse_ident(f);
Ast *tied_name = nullptr;
Ast *type = nullptr;
Ast *value = nullptr;
if (allow_token(f, Token_ArrowRight)) {
tied_name = parse_ident(f);
}
if (allow_token(f, Token_Colon)) {
type = parse_type(f);
}
if (allow_token(f, Token_Eq)) {
if (f->curr_token.kind == Token_Ident) {
value = parse_ident(f);
} else if (f->curr_token.kind == Token_Mod) {
value = parse_asm_register(f);
} else {
error(f->curr_token, "Expected a register or scratch parameter");
Ast *dummy = parse_expr(f, true);
gb_unused(dummy);
}
}
if (tied_name != nullptr) {
if (type != nullptr) {
syntax_error(f->curr_token, "An asm specification for tied values cannot declare a type");
}
} else if (type == nullptr && value == nullptr) {
syntax_error(f->curr_token, "An asm specification must specify at least either a type or a value if the value is not tied");
}
Array<Ast *> directives = {};
directives.allocator = heap_allocator();
while (f->curr_token.kind == Token_Hash) {
Token token = expect_token(f, Token_Hash);
Token name = expect_token_after(f, Token_Ident, "hash for directive");
if (name.kind == Token_Ident) {
Ast *directive = ast_basic_directive(f, token, name);
array_add(&directives, directive);
}
}
Ast *spec = alloc_ast_node(f, Ast_AsmSpec);
spec->AsmSpec.name = name;
spec->AsmSpec.tied_name = tied_name;
spec->AsmSpec.type = type;
spec->AsmSpec.value = value;
spec->AsmSpec.directives = directives;
return spec;
}
gb_internal Ast *parse_asm_template(AstFile *f) {
Token token = expect_token(f, Token_asm);
@@ -2718,47 +2774,14 @@ gb_internal Ast *parse_asm_template(AstFile *f) {
f->curr_token.kind != Token_EOF) {
Ast *spec = nullptr;
if (f->curr_token.kind == Token_Ident) {
Ast *name = parse_ident(f);
Ast *tied_name = nullptr;
Ast *type = nullptr;
Ast *value = nullptr;
if (allow_token(f, Token_ArrowRight)) {
tied_name = parse_ident(f);
}
if (allow_token(f, Token_Colon)) {
type = parse_type(f);
}
if (allow_token(f, Token_Eq)) {
if (f->curr_token.kind == Token_Ident) {
value = parse_ident(f);
} else if (f->curr_token.kind == Token_Mod) {
value = parse_asm_register(f);
} else {
error(f->curr_token, "Expected a register or scratch parameter");
Ast *dummy = parse_expr(f, true);
gb_unused(dummy);
}
}
if (tied_name != nullptr) {
if (type != nullptr) {
syntax_error(f->curr_token, "An asm specification for tied values cannot declare a type");
}
} else if (type == nullptr && value == nullptr) {
syntax_error(f->curr_token, "An asm specification must specify at least either a type or a value if the value is not tied");
}
spec = alloc_ast_node(f, Ast_AsmSpec);
spec->AsmSpec.name = name;
spec->AsmSpec.tied_name = tied_name;
spec->AsmSpec.type = type;
spec->AsmSpec.value = value;
spec = parse_asm_spec(f);
} else if (f->curr_token.kind == Token_Hash) {
Token hash = expect_token(f, Token_Hash);
Token name = expect_token(f, Token_Ident);
if (name.string == "volatile" ||
name.string == "align_stack") {
name.string == "align_stack" ||
name.string == "pure") {
Ast *clobber = alloc_ast_node(f, Ast_AsmClobber);
clobber->AsmClobber.token = hash;
clobber->AsmClobber.name = name;
+6 -4
View File
@@ -489,10 +489,11 @@ struct AstSplitArgs {
Token flag; \
}) \
AST_KIND(AsmSpec, "asm specification", struct { \
Ast *name; \
Ast *tied_name; \
Ast *type; \
Ast *value; \
Ast * name; \
Ast * tied_name; \
Ast * type; \
Ast * value; \
Array<Ast *> directives; \
}) \
AST_KIND(AsmClobber, "asm clobber", struct { \
Token token; \
@@ -509,6 +510,7 @@ struct AstSplitArgs {
u16 mnemonic; \
u8 suffix_flags; \
i32 valid_form_index; \
struct AsmInstructionFacts *facts; \
}) \
AST_KIND(AsmMemoryOperand, "asm memory operand", struct { \
Token open; \
+14
View File
@@ -293,6 +293,20 @@ gb_internal String string_split_iterator(String_Iterator *it, const char sep) {
return substring(it->str, start, end);
}
// NOTE: `string_split_iterator` returns a zero-length `String` both for an empty element and at
// exhaustion, so a loop that stops on an empty result stops at the first empty element instead.
// This skips empty elements and stops only once the iterator is exhausted.
gb_internal bool string_split_iterator_next(String_Iterator *it, char const sep, String *str_) {
while (it->pos < it->str.len) {
String str = string_split_iterator(it, sep);
if (str.len != 0) {
*str_ = str;
return true;
}
}
return false;
}
gb_internal gb_inline bool is_separator(u8 const &ch) {
return (ch == '/' || ch == '\\');
}
+2 -3
View File
@@ -3640,9 +3640,8 @@ gb_internal int matched_target_features(TypeProc *t) {
int matches = 0;
String_Iterator it = {t->require_target_feature, 0};
for (;;) {
String str = string_split_iterator(&it, ',');
if (str == "") break;
String str = {};
while (string_split_iterator_next(&it, ',', &str)) {
if (check_target_feature_is_valid_for_target_arch(str, nullptr)) {
matches += 1;
}
+5 -2
View File
@@ -244,8 +244,11 @@ wake_up :: proc(t: ^testing.T) {
}, context)
defer thread.destroy(thr)
// Should block forever until the thread calling wake_up will make it return.
ev(t, nbio.tick(), nil)
// A tick can return without progress; loop until the wake is observed.
// A lost wake would block here forever and trip the fail timeout.
for !hit {
ev(t, nbio.tick(), nil)
}
e(t, hit)
nbio.remove(accept)
+15 -3
View File
@@ -245,7 +245,7 @@ And it tests big send/recv buffers being handled properly.
@(test)
poll :: proc(t: ^testing.T) {
if event_loop_guard(t) {
// testing.set_fail_timeout(t, time.Minute)
testing.set_fail_timeout(t, time.Minute)
can_recv: bool
@@ -302,13 +302,25 @@ poll :: proc(t: ^testing.T) {
on_poll1 :: proc(op: ^nbio.Operation, t: ^testing.T, can_recv: ^bool) {
ev(t, op.poll.result, nil)
// Send 4 GB of data, which in my experience causes a Would_Block error because we filled up the internal buffer.
// Fill the socket until sending actually blocks. How much that takes depends
// on the machine's socket buffers, so keep sending rather than assuming a
// fixed amount does it. Nothing is reading yet, so this terminates.
buf, mem_err := make([]byte, mem.Gigabyte*4, context.temp_allocator)
ev(t, mem_err, nil)
// Use `core:net` as example external code that doesn't care about the event loop.
net.set_blocking(op.poll.socket, false)
n, send_err := net.send(op.poll.socket, buf)
n: int
send_err: net.Network_Error
for _ in 0..<16 {
sent: int
sent, send_err = net.send(op.poll.socket, buf)
n += sent
if send_err != nil {
break
}
}
ev(t, send_err, net.TCP_Send_Error.Would_Block)
log.debugf("blocking after %M", n)
+24 -7
View File
@@ -58,7 +58,12 @@ immediate_remove_of_sendfile :: proc(t: ^testing.T) {
}
on_recv :: proc(op: ^nbio.Operation, t: ^testing.T) {
ev(t, op.recv.err, nil)
// The server cancelled a sendfile that had already put bytes on the wire and
// then closed, which ends the connection with a reset rather than gracefully
// often enough that both have to be accepted here.
if op.recv.err != nil {
ev(t, op.recv.err, net.TCP_Recv_Error.Connection_Closed)
}
nbio.close(op.recv.socket.(net.TCP_Socket))
}
@@ -126,7 +131,12 @@ immediate_remove_of_sendfile_without_stat :: proc(t: ^testing.T) {
}
on_recv :: proc(op: ^nbio.Operation, t: ^testing.T) {
ev(t, op.recv.err, nil)
// The server cancelled a sendfile that had already put bytes on the wire and
// then closed, which ends the connection with a reset rather than gracefully
// often enough that both have to be accepted here.
if op.recv.err != nil {
ev(t, op.recv.err, net.TCP_Recv_Error.Connection_Closed)
}
nbio.close(op.recv.socket.(net.TCP_Socket))
}
@@ -212,13 +222,18 @@ remove_multiple_poll :: proc(t: ^testing.T) {
if event_loop_guard(t) {
testing.set_fail_timeout(t, time.Minute)
sock, ep := open_next_available_local_port(t)
defer nbio.close(sock)
// Two sockets rather than two polls on one socket: only one poll per socket is
// delivered on Windows, and what this tests is removal, not that.
removed_sock, removed_ep := open_next_available_local_port(t)
defer nbio.close(removed_sock)
kept_sock, kept_ep := open_next_available_local_port(t)
defer nbio.close(kept_sock)
hit: bool
first := nbio.poll(sock, .Receive, on_poll)
nbio.poll_poly2(sock, .Receive, t, &hit, on_poll2)
first := nbio.poll(removed_sock, .Receive, on_poll)
nbio.poll_poly2(kept_sock, .Receive, t, &hit, on_poll2)
on_poll :: proc(op: ^nbio.Operation) {
log.error("shouldn't be called")
@@ -235,7 +250,9 @@ remove_multiple_poll :: proc(t: ^testing.T) {
ev(t, nbio.tick(0), nil)
nbio.dial_poly(ep, t, on_dial)
// Make both readable, the removed poll must still not fire.
nbio.dial_poly(removed_ep, t, on_dial)
nbio.dial_poly(kept_ep, t, on_dial)
on_dial :: proc(op: ^nbio.Operation, t: ^testing.T) {
ev(t, op.dial.err, nil)
+105
View File
@@ -0,0 +1,105 @@
package test_internal
import "core:testing"
// % operator (truncated remainder)
// remainder = x - y * trunc(x / y)
@(private="file")
trunc_mod :: proc(x, y: $T) -> T {
return x - y*(x/y)
}
// this seems to prevent folding at least at -o:minimal
@(private="file")
not_const :: #force_no_inline proc(v: $T) -> T { return v }
@(test)
mod_i8_exhaustive :: proc(t: ^testing.T) {
for i in -128..=127 {
for j in -128..=127 {
if j == 0 { continue }
// min(T) % -1 == 0 is tested in mod_exception,
// the trunc_mod reference itself would trap here
if i == -128 && j == -1 { continue }
x, y := i8(i), i8(j)
got := x % y
want := trunc_mod(x, y)
testing.expectf(t, got == want, "%v %% %v == %v, want %v", x, y, got, want)
}
}
}
@(test)
mod_exception :: proc(t: ^testing.T) {
// min(T) % -1 is 0, which is what the constant folder answers
#assert(min(i8) % i8(-1) == 0)
#assert(min(i16) % i16(-1) == 0)
#assert(min(i32) % i32(-1) == 0)
#assert(min(i64) % i64(-1) == 0)
#assert(min(i128) % i128(-1) == 0)
check :: proc(t: ^testing.T, $T: typeid, loc := #caller_location) {
x, y := not_const(min(T)), not_const(T(-1))
testing.expectf(t, x % y == 0, "min(%v) %% -1 (rt divisor) == %v, want 0", typeid_of(T), x % y, loc = loc)
testing.expectf(t, x % -1 == 0, "min(%v) %% -1 (const divisor) == %v, want 0", typeid_of(T), x % -1, loc = loc)
}
check(t, i8)
check(t, i16)
check(t, i32)
check(t, i64)
check(t, i128)
}
@(test)
mod_exception_vec :: proc(t: ^testing.T) {
{
// [4]i32 emits `srem <4 x i32>`
x := not_const([4]i32{min(i32), 0, -7, 5})
y := not_const([4]i32{-1, -1, -1, -1})
testing.expect_value(t, x % y, [4]i32{0, 0, 0, 0})
}
{
// [16]i32 emits scalar `srem i32`, which is the other call site
x, y: [16]i32
for i in 0..<16 {
x[i] = i == 0 ? min(i32) : i32(i) - 8
y[i] = -1
}
testing.expect_value(t, not_const(x) % not_const(y), [16]i32{})
}
}
@(test)
mod_assign :: proc(t: ^testing.T) {
// %= must agree with %
{
x := not_const(min(i32))
y := not_const(i32(-1))
x %= y
testing.expect_value(t, x, 0)
}
{
x := not_const(i64(-17))
y := not_const(i64(5))
x %= y
testing.expect_value(t, x, -17 % 5)
}
}
@(test)
mod_unsigned_unchanged :: proc(t: ^testing.T) {
// the guard is signed-only; unsigned max is all-ones and must stay a real divisor
{
x, y := not_const(max(u32)), not_const(max(u32))
testing.expect_value(t, x % y, 0)
}
{
x, y := not_const(u32(7)), not_const(max(u32))
testing.expect_value(t, x % y, 7)
}
{
x, y := not_const(u8(200)), not_const(u8(255))
testing.expect_value(t, x % y, 200)
}
}
-3
View File
@@ -43,12 +43,9 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused
..\..\..\odin test ..\test_issue_7008.odin %COMMON% || exit /b
..\..\..\odin check ..\test_issue_7012.odin -no-entry-point %COMMON% || exit /b
..\..\..\odin check ..\test_issue_7260.odin -no-entry-point %COMMON% || exit /b
..\..\..\odin check ..\test_issue_asm_named_register_slot.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "8" || exit /b
..\..\..\odin check ..\test_issue_ellipsis_type_call.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "10" || exit /b
..\..\..\odin check ..\test_issue_foreign_redeclaration.odin -no-entry-point %COMMON% || exit /b
..\..\..\odin check ..\test_issue_foreign_redeclaration_mismatch.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b
..\..\..\odin check ..\test_issue_asm_rip_register.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "6" || exit /b
..\..\..\odin check ..\test_issue_asm_template_as_value.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "10" || exit /b
..\..\..\odin build ..\test_issue_7037.odin %COMMON% -o:none || exit /b
..\..\..\odin build ..\test_issue_7188.odin %COMMON% || exit /b
clang -c ..\test_issue_sysv_abi.c -o test_issue_sysv_abi_c.o || exit /b
+2 -25
View File
@@ -91,20 +91,13 @@ $ODIN check ../test_issue_6979.odin -no-entry-point $COMMON_CHECK
$ODIN test ../test_issue_7008.odin $COMMON
$ODIN check ../test_issue_7012.odin -no-entry-point $COMMON_CHECK
$ODIN build ../test_issue_7037.odin $COMMON -o:none
$ODIN check ../test_issue_7429.odin $COMMON_CHECK
$ODIN test ../test_issue_7356.odin $COMMON
$ODIN build ../test_issue_7167.odin $COMMON
$ODIN build ../test_issue_7188.odin $COMMON
$ODIN check ../test_issue_7260.odin -no-entry-point $COMMON_CHECK
$ODIN test ../test_issue_bool_to_be_conversion.odin $COMMON
# `asm` templates are amd64-only, so this file is empty on every other architecture
if [[ "$(uname -m)" == "x86_64" || "$(uname -m)" == "amd64" ]]; then
if [[ $($ODIN check ../test_issue_asm_named_register_slot.odin -no-entry-point $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 8 ]]; then
echo "SUCCESSFUL 1/1"
else
echo "SUCCESSFUL 0/1"
exit 1
fi
fi
$ODIN check ../test_issue_foreign_redeclaration.odin -no-entry-point $COMMON_CHECK
if [[ $($ODIN check ../test_issue_foreign_redeclaration_mismatch.odin -no-entry-point $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]]; then
echo "SUCCESSFUL 1/1"
@@ -120,22 +113,6 @@ else
exit 1
fi
# `asm` templates are amd64-only, so this file is empty on every other architecture
if [[ "$(uname -m)" == "x86_64" || "$(uname -m)" == "amd64" ]]; then
if [[ $($ODIN check ../test_issue_asm_rip_register.odin -no-entry-point $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 6 ]]; then
echo "SUCCESSFUL 1/1"
else
echo "SUCCESSFUL 0/1"
exit 1
fi
if [[ $($ODIN check ../test_issue_asm_template_as_value.odin -no-entry-point $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 10 ]]; then
echo "SUCCESSFUL 1/1"
else
echo "SUCCESSFUL 0/1"
exit 1
fi
fi
if [[ $($ODIN build ../test_issue_7108.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]]; then
echo "SUCCESSFUL 1/1"
else
+15
View File
@@ -0,0 +1,15 @@
// Tests issue #7429: local distinct types in procedure literal values must have
// unique canonical names.
// https://github.com/odin-lang/Odin/issues/7429
package test_issues
main :: proc() {
_ = proc() {
Foo :: distinct string
_ = typeid_of(Foo)
}
_ = proc() {
Foo :: distinct string
_ = typeid_of(Foo)
}
}
@@ -1,41 +0,0 @@
#+build amd64
// A slot that only a named hardware register can fill (segment/control/debug/x87/MMX)
// carries no width and no register class, so it used to absorb any operand at all and
// hand the backend an instruction that does not encode.
package test_issues
// Rejected: none of these widths pair up, and only `mov`'s segment-register forms ever
// admitted them.
bad_64_32 :: asm(a: i64) -> (r: i32) { mov r, a; }
bad_8_16 :: asm(a: u8) -> (r: u16) { mov r, a; }
bad_16_8 :: asm(a: u16) -> (r: u8) { mov r, a; }
bad_64_8 :: asm(a: u64) -> (r: u8) { mov r, a; }
// Accepted: equal widths, regardless of signedness or pointer spelling.
ok_32 :: asm(a: i32) -> (r: u32) { mov r, a; }
ok_64 :: asm(a: u64) -> (r: i64) { mov r, a; }
ok_ptr :: asm(a: rawptr) -> (r: ^i32) { mov r, a; }
// Accepted: the named registers those forms are actually for.
ok_seg :: asm(a: u64) -> (r: u64) { mov %ds, a; mov r, a; }
ok_ctrl :: asm(a: u64) -> (r: u64) { mov %cr0, a; mov r, a; }
ok_dbg :: asm(a: u64) -> (r: u64) { mov %dr0, a; mov r, a; }
use :: proc() {
a8: u8
a16: u16
a32: i32
a64: i64
au64: u64
ap: rawptr
_ = bad_64_32(a64)
_ = bad_8_16(a8)
_ = bad_16_8(a16)
_ = bad_64_8(au64)
_ = ok_32(a32)
_ = ok_64(au64)
_ = ok_ptr(ap)
_ = ok_seg(au64)
_ = ok_ctrl(au64)
_ = ok_dbg(au64)
}
@@ -1,15 +0,0 @@
#+build amd64
// `%rip` is in the amd64 register table, but its class carries no width, so the checker's width
// switch reached its `GB_PANIC` default arm and aborted with SIGILL instead of diagnosing. Every
// position that can name a register reached it, including `[%rip + disp]`.
package test_issues
rip_src :: asm() -> (v: u64) { mov v, %rip; }
rip_dst :: asm(x: u64) { mov %rip, x; }
rip_mem :: asm() { mov %rax, [%rip + 8]; }
rip_clob :: asm() [#clobber %rip] { nop; }
rip_in :: asm(x: u64) [x = %rip] { nop; }
rip_out :: asm() -> (r: u64) [r = %rip] { nop; }
// the nearest special-purpose register that does carry a class has to keep checking cleanly
rsp_ok :: asm() -> (v: u64) { mov v, %rsp; }
@@ -1,48 +0,0 @@
#+build amd64
// A named asm template got a plain `Addressing_Value`, so every value gate let it through:
// a cast, a transmute, an `auto_cast`, a blank assignment, a polymorphic parameter and a
// comparison against `nil` all passed the checker and then aborted the compiler in the
// backend, which has no value to lower for a template. Only a direct call and a listing in
// an `asm` group are legal.
package test_issues
t :: asm(a: i32) -> (v: i32) { mov v, a; }
a32 :: asm(a: i32) -> (v: i32) { mov v, a; }
a64 :: asm(a: i64) -> (v: i64) { mov v, a; }
g :: asm { a32, a64 }
G := cast(rawptr)(t)
take_rawptr :: proc(p: rawptr) {
_ = p
}
poly :: proc(x: $T) {
_ = size_of(T)
}
bad :: proc() {
_ = cast(proc "c" (i32) -> i32)(t)
_ = transmute(proc "c" (i32) -> i32)(t)
_ = cast(rawptr)(t)
_ = transmute(uintptr)(t)
take_rawptr(auto_cast t)
take_rawptr(cast(rawptr)(t))
_ = t
poly(t)
if t == nil {
take_rawptr(nil)
}
}
// these forms must remain valid
good :: proc() -> i32 {
x := t(1)
y := (t)(2)
z := g(i32(3))
w := g(i64(4))
v := asm(a: i32) -> (v: i32) { mov v, a; }(5)
take_rawptr(G)
return x + y + z + i32(w) + v
}
@@ -0,0 +1,37 @@
package test_issues
import "core:testing"
// Converting a boolean to a big-endian integer skipped the endian fixup that the integer source
// path performs, so the result carried a native bit pattern labelled big-endian. The checker
// folded the same conversion to the right value, so only the runtime disagreed.
@(test)
bool_to_big_endian :: proc(t: ^testing.T) {
b: bool = true
f: bool = false
b8v: b8 = true
b16v: b16 = true
b32v: b32 = true
b64v: b64 = true
i: int = 1
testing.expect_value(t, int(i16be(b)), 1)
testing.expect_value(t, int(u32be(b)), 1)
testing.expect_value(t, int(u64be(b)), 1)
testing.expect_value(t, int(u128be(b)), 1)
testing.expect_value(t, int(i16be(f)), 0)
testing.expect_value(t, int(i16be(b8v)), 1)
testing.expect_value(t, int(i16be(b16v)), 1)
testing.expect_value(t, int(i16be(b32v)), 1)
testing.expect_value(t, int(i16be(b64v)), 1)
// the little-endian target and the integer source were already correct
testing.expect_value(t, int(i16le(b)), 1)
testing.expect_value(t, int(i16be(i)), 1)
// the bytes have to actually be big-endian, not a native pattern relabelled
testing.expect_value(t, transmute([4]u8)u32be(b), transmute([4]u8)u32be(i))
testing.expect_value(t, transmute([4]u8)u32be(b), [4]u8{0, 0, 0, 1})
}
+1 -1
View File
@@ -663,7 +663,7 @@ MouseButton :: enum c.int {
MIDDLE = 2, // Mouse button middle (pressed wheel)
SIDE = 3, // Mouse button side (advanced mouse device)
EXTRA = 4, // Mouse button extra (advanced mouse device)
FORWARD = 5, // Mouse button fordward (advanced mouse device)
FORWARD = 5, // Mouse button forward (advanced mouse device)
BACK = 6, // Mouse button back (advanced mouse device)
}