Skip to content

Commit

Permalink
ᗜˬᗜ
Browse files Browse the repository at this point in the history
  • Loading branch information
ascpixi committed May 18, 2024
1 parent 279fb8b commit 7ca9342
Show file tree
Hide file tree
Showing 13 changed files with 256 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
lib*.a
demo/demo
demo/*.exe
.vscode
/obj
16 changes: 16 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# is-odd-jit License Agreement
### Inheritance Statement
This License Agreement for the "is-odd-jit" project (the "License") is an extension and continuation of the license terms set forth by the original "is-odd" project. The "is-odd-jit" project inherits its licensing framework, stipulations, and legal enforceability from the "is-odd" project. By accessing, using, modifying, or distributing the "is-odd-jit" software, you acknowledge and agree that this License is bound by and subject to the terms of the "is-odd" project's license.

The original "is-odd" license serves as the foundational legal document governing the use, modification, and distribution of the "is-odd-jit" software. All clauses, conditions, restrictions, and legal obligations contained within the "is-odd" license are fully applicable to the "is-odd-jit" project. Any interpretation, enforcement, or adjudication of the terms of this License shall be conducted with reference to the original "is-odd" license.

For the avoidance of doubt, the rights, responsibilities, and restrictions that apply to the "is-odd" project, as specified in its license, are hereby fully transferred to and incorporated within the "is-odd-jit" project. Users, developers, and distributors of the "is-odd-jit" software are required to adhere strictly to the licensing terms established by the "is-odd" project.

This inheritance of licensing terms ensures consistency and continuity in the legal framework governing the "is-odd" and "is-odd-jit" projects. Any provisions, disclaimers, and conditions originally articulated in the "is-odd" license are fully enforceable within the context of the "is-odd-jit" project. As such, any use, modification, or distribution of the "is-odd-jit" software must be conducted in compliance with the inherited license terms.

It is important to note that the "is-odd-jit" project does not introduce any new licensing terms or modify the existing terms of the "is-odd" license. Instead, it upholds and perpetuates the legal and ethical standards set forth by the "is-odd" project. This ensures that the intellectual property rights, restrictions on usage, and legal disclaimers remain intact and are consistently applied to both projects.

By engaging with the "is-odd-jit" software, you confirm your acceptance of these inherited terms and acknowledge that any legal actions, interpretations, or disputes will be governed by the original "is-odd" license. This acknowledgment reinforces the binding nature of the inherited license and underscores your obligation to comply with its terms.

## is-odd License
Do not use
40 changes: 40 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
CC = gcc

CFLAGS = -Wall -Werror -fPIC

SRC_DIR = src
OBJ_DIR = obj/$(OS)

SOURCES = $(wildcard $(SRC_DIR)/*.c)
OBJECTS = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SOURCES))

OUTPUT_NAME = libisodd-$(OS).a

all: $(OBJ_DIR) $(OUTPUT_NAME)

# Create object directory if it doesn't exist
$(OBJ_DIR):
mkdir -p $(OBJ_DIR)

$(OUTPUT_NAME): $(OBJECTS)
$(AR) rcs $@ $^

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CFLAGS) $(OS_CFLAGS) -c $< -o $@

# Detect OS and set appropriate flags
ifeq ($(OS),windows)
CC = x86_64-w64-mingw32-gcc
AR = x86_64-w64-mingw32-ar
OS_CFLAGS = -DWIN32 -I/usr/share/mingw-w64/include/
LDFLAGS = -Wl,--out-implib,libisodd.a
else ifeq ($(OS),linux)
OS_CFLAGS =
else
$(error Unknown OS specified. Please set 'OS' to one of: 'windows', 'linux')
endif

clean:
rm -rf $(OBJ_DIR) $(OUTPUT_NAME) $(DYNAMIC_LIB)

.PHONY: all clean
18 changes: 18 additions & 0 deletions demo/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include <stdint.h>
#include <stdio.h>

#include "../include/isodd.h"

int main() {
while (1) {
printf("input: ");

uint16_t value;
if (scanf("%hu", &value) != 1) {
fprintf(stderr, "invalid input, exiting\n");
return 1;
}

printf(is_odd(value) ? "%hu is odd\n" : "%hu is even\n", value);
}
}
3 changes: 3 additions & 0 deletions demo/run-linux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(cd .. && make OS=linux)
gcc -o demo main.c -L.. -lisodd-linux
./demo
3 changes: 3 additions & 0 deletions demo/run-windows.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
wsl make -C .. OS=windows
wsl x86_64-w64-mingw32-gcc -o demo.exe main.c -L.. -lisodd-windows -lkernel32
demo.exe
7 changes: 7 additions & 0 deletions include/isodd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#include <stdbool.h>
#include <stdint.h>

// Determines whether a number is even or odd.
extern bool is_odd(uint64_t value);
11 changes: 11 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# is-odd-jit
the **is-odd-jit** library is a *blazing* fast way to tell whether a number is even or odd - now leveraging **just-in-time (JIT) compilation!** (ノ◕ヮ◕)ノ*:・゚✧

> [!TIP]
> you also may be interested in the previous, web version, [ascpixi/is-odd](https://github.com/ascpixi/is-odd)!
in order to use is-odd-jit, compile the library under Linux via `make OS=linux` or `make OS=windows` in the root repository directory - for Windows, you'll also need to run `sudo apt-get install gcc-mingw-w64`.

this will produce a static library named `libisodd-<os>.a` (where `<os>` is the OS the library was compiled for), that you can reference like any other! see the [demo](./demo/) for more details!

currently, the only supported architecture is **x86-64**, and the supported ABIs are System V (most Unices) and the Microsoft x64 (Windows).
16 changes: 16 additions & 0 deletions src/abi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "arch.h"

#ifdef __x86_64
#ifdef WIN32
// Microsoft x64 ABI
#define CALLCONV_ARG1 X86_RM_CX
#else
// System V x64 ABI
#define CALLCONV_ARG1 X86_RM_DI
#endif
#else
#error Unsupported architecture.
#endif

14 changes: 14 additions & 0 deletions src/arch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#ifdef __x86_64
#define X86_RM_AX 0
#define X86_RM_CX 1
#define X86_RM_DX 2
#define X86_RM_BX 3
#define X86_RM_SP 4
#define X86_RM_BP 5
#define X86_RM_SI 6
#define X86_RM_DI 7

#define X86_OPSIZE_OVERRIDE 0x66
#endif
86 changes: 86 additions & 0 deletions src/isodd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "abi.h"
#include "os.h"

typedef bool (*fnptr_is_odd_jitted)(uint64_t value);

static fnptr_is_odd_jitted emitted_code;

#if __x86_64
static fnptr_is_odd_jitted compile() {
// the maximum size for the generated function is equal to:
// - for each value...
// - 5 bytes for CMP
// - 6 bytes for JE rel32
// - ...and for the two outcomes:
// - even:
// - xor rax, rax 3 bytes
// - ret 1 byte
// - odd:
// - mov rax, 1 7 bytes
// - ret 1 byte
// we could encode the JE as rel8 or rel16, but this would complicate matters
// (calculation of jump distances would be harder), and I really cannot be bothered! ;3c

uint32_t max_size = ((5 + 6) * 0xffff) + 8 + 4;

void *memory = malloc(max_size);
uint8_t *current = memory;

for (uint16_t i = 0; i < 0xffff; i++) {
// CMP r/m16, imm16 [81 /7 iw]
*current++ = X86_OPSIZE_OVERRIDE;
*current++ = 0x81;
*current++ = 0b11111000 | CALLCONV_ARG1;
*(uint16_t*)current = i;
current += sizeof(uint16_t);

uint32_t jmp_distance = (0xffff - i) * (5 + 6) - 11;
uint32_t target = (i % 2 == 0) ? 0 : 4; // even? then offset is 0; else, skip "xor rax, rax" and "ret"

// JE imm32 [0F 84 cd]
*current++ = 0x0F;
*current++ = 0x84;
*(uint32_t*)current = jmp_distance + target;
current += sizeof(uint32_t);
}

// Emit return functions

// +0 even:
*current++ = 0x48; // xor rax, rax
*current++ = 0x31;
*current++ = 0xC0;

*current++ = 0xC3; // ret

// +4 odd:
*current++ = 0x48; // mov rax, 1
*current++ = 0xC7;
*current++ = 0xC0;
*(uint32_t*)current = 1;
current += sizeof(uint32_t);

*current++ = 0xC3; // ret

// Convert the read-write memory to read-execute (will copy)
void *fn = os_make_executable(max_size, memory);
free(memory);
return fn;
}
#else
#error Code generation is not defined for this architecture.
#endif

bool is_odd(uint64_t value) {
if (emitted_code != NULL)
return emitted_code(value); // function was JIT-ted before

fnptr_is_odd_jitted func = compile();
emitted_code = func;
return func(value);
}

25 changes: 25 additions & 0 deletions src/os.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#if WIN32

#include <windows.h>

void* os_make_executable(size_t bytes, void* buffer) {
DWORD unused;

void *out = VirtualAlloc(NULL, bytes, MEM_COMMIT, PAGE_READWRITE);
memcpy(out, buffer, bytes);
VirtualProtect(out, bytes, PAGE_EXECUTE_READ, &unused);
return out;
}

#else

#include <sys/mman.h>
#include <string.h>

void* os_make_executable(size_t bytes, void* buffer) {
void *out = mmap(0, bytes, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memcpy(out, buffer, bytes);
return out;
}

#endif
12 changes: 12 additions & 0 deletions src/os.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <stdint.h>

// Allocates an executable memory region, copies the memory region described by
// "bytes" and "buffer", and changes the allocated buffer to be read-only.
//
// Returns the allocated buffer.
void* os_make_executable(
size_t bytes,
void *buffer
);

0 comments on commit 7ca9342

Please sign in to comment.