Skip to content

Commit

Permalink
Implement some integer operations in C
Browse files Browse the repository at this point in the history
These use a Rust/runtime fallback for compilers that don't provide
checked integer operations.
  • Loading branch information
yorickpeterse committed Nov 15, 2022
1 parent 5a8f063 commit 445b5a5
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 122 deletions.
4 changes: 4 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
BasedOnStyle: LLVM
IndentPPDirectives: AfterHash
IndentWidth: 2
188 changes: 188 additions & 0 deletions include/inko.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#ifndef INKO_H
#define INKO_H

// We require a two's complement C compiler so we can emulate certain checked
// integer operations without running into undefined behaviour.
#if (-1 & 3) != 3
# error "Inko requires the C compiler to use two's complement for integers"
#endif

#include "stdbool.h"
#include "stddef.h"
#include "stdint.h"

#if defined(_MSC_VER)
# include "Intsafe.h"
#endif

#define RUST_STRING_SIZE 24
#define RUST_VEC_SIZE 24

#if defined(__clang__)
# define NO_RETURN __attribute__((noreturn))
#elif defined(__GNUC__)
# define NO_RETURN __attribute__((noreturn))
#elif defined(_MSC_VER)
# define NO_RETURN __declspec(noreturn)
#else
# define NO_RETURN
#endif

#define INKO_INT_MASK 0x1
#define INKO_INT_READ(val) \
((((size_t)val & INKO_INT_MASK) == INKO_INT_MASK) ? ((int64_t)val) >> 1 \
: val->value)

#define KIND_REGULAR 0
#define KIND_PERMANENT 1
#define KIND_REF 2
#define KIND_ATOMIC 3

#define RESULT_OK 0
#define RESULT_ERROR 1
#define RESULT_TIMEOUT 2
#define RESULT_NONE 3

typedef struct InkoResult {
uint8_t tag;
void *value;
} InkoResult;

typedef struct InkoMethod {
uint32_t hash;
void (*code)();
} InkoMethod;

typedef struct InkoClass {
uint8_t name[RUST_STRING_SIZE];
size_t instance_size;
uint16_t method_slots;
void *methods[];
} InkoClass;

typedef struct Header {
InkoClass *class;
uint8_t kind;
uint16_t references;
} Header;

typedef struct InkoArray {
Header header;
uint8_t value[RUST_VEC_SIZE];
} InkoArray;

typedef struct InkoByteArray {
Header header;
uint8_t value[RUST_VEC_SIZE];
} InkoByteArray;

typedef struct InkoInt {
Header header;
int64_t value;
} InkoInt;

typedef struct InkoBool {
Header header;
bool value;
} InkoBool;

typedef struct InkoNil {
Header header;
} InkoNil;

typedef struct InkoFloat {
Header header;
double value;
} InkoFloat;

typedef struct InkoString {
Header header;
uint8_t value[RUST_VEC_SIZE];
} InkoString;

typedef struct InkoProcess {
Header header;
uint64_t run_lock;
uint8_t write[16];
InkoResult message_result;
void *stack_pointer;
uint8_t stack[16];
uint8_t call_stack[RUST_VEC_SIZE];
void *thread;
uint8_t state[56];
void *fields[];
} InkoProcess;

typedef struct InkoFuture {
Header header;
void *value;
} InkoFuture;

typedef struct InkoContext {
void *state;
InkoProcess *process;
void **arguments;
} InkoContext;

typedef struct InkoMethodCounts {
uint16_t int_class;
uint16_t float_class;
uint16_t string_class;
uint16_t array_class;
uint16_t boolean_class;
uint16_t nil_class;
uint16_t byte_array_class;
uint16_t future_class;
} InkoMethodCounts;

typedef struct InkoStackFrame {
InkoString *name;
InkoString *path;
InkoInt *line;
} InkoStackFrame;

extern int64_t inko_int64_add(int64_t, int64_t, int64_t *);
extern int64_t inko_int64_sub(int64_t, int64_t, int64_t *);
extern int64_t inko_int64_mul(int64_t, int64_t, int64_t *);

bool int64_add(int64_t lhs, int64_t rhs, int64_t *result) {
#if defined(__clang__) || defined(__GNUC__)
return __builtin_saddll_overflow(lhs, rhs, (long long *)result);
#elif defined(_MSC_VER)
return LongLongAdd(lhs, rhs, (long long *)result) != S_OK;
#else
return inko_int64_add(lhs, rhs, result);
#endif
}

bool int64_sub(int64_t lhs, int64_t rhs, int64_t *result) {
#if defined(__clang__) || defined(__GNUC__)
return __builtin_ssubll_overflow(lhs, rhs, (long long *)result);
#elif defined(_MSC_VER)
return LongLongSub(lhs, rhs, (long long *)result) != S_OK;
#else
return inko_int64_sub(lhs, rhs, result);
#endif
}

bool int64_div(int64_t lhs, int64_t rhs, int64_t *result) {
if (rhs == 0 || (lhs == INT64_MIN && rhs == -1)) {
return true;
}

*result = lhs / rhs;

return false;
}

bool int64_mul(int64_t lhs, int64_t rhs, int64_t *result) {
#if defined(__clang__) || defined(__GNUC__)
return __builtin_smulll_overflow(lhs, rhs, (long long *)result);
#elif defined(_MSC_VER)
return LongLongMult(lhs, rhs, (long long *)result) != S_OK;
#else
return inko_int64_mul(lhs, rhs, result);
#endif
}

#endif
107 changes: 13 additions & 94 deletions test.c
Original file line number Diff line number Diff line change
@@ -1,101 +1,19 @@
#include "stdbool.h"
#include "inko.h"
#include "stddef.h"
#include "stdint.h"
#include "stdio.h"
#include "string.h"
#include "time.h"

#if defined(__clang__)
#define NO_RETURN __attribute__((noreturn))
#elif defined(__GNUC__)
#define NO_RETURN __attribute__((noreturn))
#elif defined(_MSC_VER)
#define NO_RETURN __declspec(noreturn)
#else
#define NO_RETURN
#endif

#define INKO_INT_MASK 0x1
#define INKO_INT_READ(val) \
((((size_t)val & INKO_INT_MASK) == INKO_INT_MASK) ? ((int64_t)val) >> 1 \
: val->value)

#define RESULT_ERROR 1
#define RESULT_TIMEOUT 2

typedef enum Kind { KIND_REGULAR, KIND_PERMANENT, KIND_REF, KIND_ATOMIC } Kind;

typedef void *InkoRuntime;

typedef struct Result {
uint8_t tag;
void *value;
} Result;

typedef struct Header {
void *class;
bool atomic;
uint16_t references;
} Header;

// TODO: exposing the exact layout to native code is tricky, as we don't control
// many of the type layouts (e.g. technically the Mutex layout can change).
//
// To handle this we have to make sure that the compiler and runtime are
// compiled using the same version of Rust.
typedef struct InkoClass {
uint8_t name[24];
size_t instance_size;
uint16_t method_slots;
void *methods[];
} InkoClass;

typedef struct InkoProcess {
Header header;
uint16_t reductions;
uint64_t run_lock;
uint8_t write[16];
Result message_result;
void *stack_pointer;
uint8_t stack[16];
uint8_t call_stack[24];
void *thread;
uint8_t state[56];
void *fields[];
} InkoProcess;

typedef struct InkoInt {
Header header;
int64_t value;
} InkoInt;

typedef InkoClass *ClassPointer;
typedef void *StatePointer;
typedef InkoProcess *ProcessPointer;

typedef struct InkoContext {
StatePointer state;
ProcessPointer process;
void **arguments;
} InkoContext;

typedef struct InkoMethodCounts {
uint16_t int_class;
uint16_t float_class;
uint16_t string_class;
uint16_t array_class;
uint16_t boolean_class;
uint16_t nil_class;
uint16_t byte_array_class;
uint16_t future_class;
} InkoMethodCounts;

typedef struct StackFrame {
void *name;
void *path;
InkoInt *line;
} StackFrame;

extern InkoRuntime *inko_runtime_new(InkoMethodCounts *);
extern StatePointer inko_runtime_state(InkoRuntime *);
extern void inko_runtime_start(InkoRuntime *, ClassPointer, void (*)());
Expand All @@ -109,7 +27,7 @@ extern void inko_class_drop(ClassPointer);
extern void inko_process_yield(ProcessPointer);
extern void inko_process_finish_message(ProcessPointer, bool);
extern NO_RETURN void inko_process_panic(ProcessPointer, char *, size_t);
extern void inko_process_push_stack_frame(ProcessPointer, StackFrame *);
extern void inko_process_push_stack_frame(ProcessPointer, InkoStackFrame *);
extern void inko_process_pop_stack_frame(ProcessPointer);

extern void *inko_string_new(StatePointer, char *, size_t);
Expand All @@ -125,15 +43,16 @@ extern InkoInt *inko_int_new_permanent(StatePointer, int64_t);
extern NO_RETURN void inko_int_overflow_error(ProcessPointer, InkoInt *,
InkoInt *, char *, size_t);

extern Result inko_child_process_spawn(ProcessPointer, void *, void *, void *,
InkoInt *, InkoInt *, InkoInt *, void *);
extern InkoResult inko_child_process_spawn(ProcessPointer, void *, void *,
void *, InkoInt *, InkoInt *,
InkoInt *, void *);

extern Result inko_child_process_wait(ProcessPointer, void *);
extern InkoResult inko_child_process_wait(ProcessPointer, void *);
extern void inko_child_process_drop(StatePointer, void *);
extern void *inko_method_new(uint32_t, void (*)());

static struct StackFrame frame1 = {0};
static struct StackFrame frame2 = {0};
static struct InkoStackFrame frame1 = {0};
static struct InkoStackFrame frame2 = {0};

void Main_main(InkoContext *ctx) {
StatePointer state = ctx->state;
Expand All @@ -143,7 +62,7 @@ void Main_main(InkoContext *ctx) {

inko_process_push_stack_frame(proc, &frame1);

void *program = inko_string_new(state, "lsx", strlen("lsx"));
void *program = inko_string_new(state, "ls", strlen("ls"));
void *args = inko_array_new(state, 1);

inko_array_push(state, args, inko_string_new(state, "/", 1));
Expand All @@ -154,10 +73,10 @@ void Main_main(InkoContext *ctx) {
InkoInt *stderr = inko_int_new(state, 1);
void *directory = inko_string_new(state, "", 0);

Result spawn_res = inko_child_process_spawn(proc, program, args, env, stdin,
stdout, stderr, directory);
InkoResult spawn_res = inko_child_process_spawn(
proc, program, args, env, stdin, stdout, stderr, directory);

if (spawn_res.tag == RESULT_ERROR) {
if (spawn_res.tag != RESULT_OK) {
char *message = "failed to spawn process";

inko_process_push_stack_frame(proc, &frame2);
Expand All @@ -166,7 +85,7 @@ void Main_main(InkoContext *ctx) {

void *child = spawn_res.value;

Result wait_res = inko_child_process_wait(proc, child);
InkoResult wait_res = inko_child_process_wait(proc, child);

if (wait_res.tag == RESULT_ERROR) {
char *message = "failed to wait for child";
Expand Down
Loading

0 comments on commit 445b5a5

Please sign in to comment.