Optimization 30

This commit is contained in:
nazrin 2025-06-15 14:45:52 +00:00
parent 51bc1395f8
commit f1e6ed4ef8
7 changed files with 101 additions and 29 deletions

View file

@ -9,6 +9,7 @@ sourcePaths
configuration "clox" {
buildOptions "betterC"
sourcePaths "src/clox"
versions "nanBoxing"
}
configuration "clox-dbg" {
@ -18,6 +19,8 @@ configuration "clox-dbg" {
sourcePaths "src/clox"
buildRequirements "requireBoundsCheck" "requireContracts"
versions "nanBoxing"
debugVersions "printCode" "traceExec"
/* debugVersions "stressGC" */

View file

@ -8,6 +8,7 @@ debug import std.stdio : writeln;
import clox.memory;
import clox.memorydbg;
import clox.obj;
import clox.util;
import clox.value;
import clox.container.dynarray;
import conf = clox.conf;
@ -29,6 +30,7 @@ struct Table(K, V){
Entry[] pool;
size_t alive;
this(size_t size){
size = npow2(size);
pool = allocatePool(size);
}
void free(){
@ -69,21 +71,21 @@ struct Table(K, V){
if(alive == 0)
return null;
Hash hash = getHash(key);
size_t index = hash % pool.length;
size_t index = hash & ((pool.length) - 1);
while(true){
const entry = pool[index];
if(entry.isAlive && entry.key == key)
return &pool[index];
if(entry.isFree)
break;
index = (index+1) % pool.length;
index = (index+1) & ((pool.length) - 1);
}
return null;
}
void opIndexAssign(V val, in K key){
checkResize();
Hash hash = getHash(key);
size_t index = hash % pool.length;
size_t index = hash & ((pool.length) - 1);
while(true){
const Entry* entry = &pool[index];
if(entry.isAlive && entry.key == key)
@ -92,7 +94,7 @@ struct Table(K, V){
alive++;
break;
}
index = (index+1) % pool.length;
index = (index+1) & ((pool.length) - 1);
}
pool[index] = Entry(cast(K)key, val);
}

View file

@ -70,3 +70,18 @@ version(D_BetterC) {} else string prettyPtr(void* ptr){
return "\033[38;2;" ~ colours[0].to!string ~ ";" ~ colours[1].to!string ~ ";" ~ colours[2].to!string ~ "m" ~ ptr.to!string ~ "\033[0m";
}
size_t npow2(size_t n){
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n + 1;
}
// https://github.com/dlang/phobos/blob/832cc465998b1ea77051cd3fd014b544442a4f8c/std/conv.d#L6396
pragma(inline, true) ref T bitCast(T, S)(ref S value) if (T.sizeof <= S.sizeof){
return *cast(T*) &value;
}

View file

@ -4,41 +4,88 @@ import core.stdc.stdio;
import clox.memory;
import clox.obj;
import clox.util;
struct Value{
enum Type{
None, Bool, Nil, Number, Obj
}
Type type;
private union Un{
bool boolean;
double number;
Obj* obj;
}
private Un un;
version(nanBoxing){
private enum QNAN = ulong(0x7ffc000000000000);
private enum SIGN_BIT = ulong(0x8000000000000000);
enum Tag{
Nil = 1, False = 2, True = 3
}
ulong data;
static Value from(double val) => Value(val.bitCast!ulong);
static Value from(bool val) => Value(val ? QNAN | Tag.True : QNAN | Tag.False);
static Value from(T)(T* val) if(__traits(compiles, val.obj)) => Value(SIGN_BIT | QNAN | val.bitCast!ulong);
static Value nil() pure => Value(QNAN | Tag.Nil);
Type type() const pure{
if(isType(Type.Number))
return Type.Number;
if(isType(Type.Bool))
return Type.Bool;
if(isType(Type.Nil))
return Type.Nil;
if(isType(Type.Obj))
return Type.Obj;
assert(0);
}
bool isType(Type type) const pure{
final switch(type){
case Type.Number: return (data & QNAN) != QNAN;
case Type.Nil: return data == nil.data;
case Type.Bool: return data == (QNAN | Tag.True) || data == (QNAN | Tag.False);
case Type.Obj: return (data & (QNAN | SIGN_BIT)) == (QNAN | SIGN_BIT);
case Type.None: assert(0);
}
}
auto as(Type type)() const pure{
static if(type != Type.None)
assert(isType(type));
static if(type == Type.Number) return data.bitCast!double;
static if(type == Type.Bool) return data == (QNAN | Tag.True);
static if(type == Type.Obj) return cast(Obj*)(data & ~(SIGN_BIT | QNAN));
static if(type == Type.None) return this;
}
} else {
private union Un{
bool boolean;
double number;
Obj* obj;
}
private Type type;
private Un un;
static Value from(double val) => Value(Type.Number, Un(number: val));
static Value from(bool val) => Value(Type.Bool, Un(boolean: val));
static Value from(T)(T* val) if(__traits(compiles, val.obj)) => Value(Type.Obj, Un(obj: cast(Obj*)val));
static Value from(Value val) => val;
static Value nil() pure => Value(Type.Nil);
bool isType(Type type) const pure => this.type == type;
auto as(Type type)() const pure{
static if(type != Type.None)
assert(isType(type));
static if(type == Type.Number) return un.number;
static if(type == Type.Bool) return un.boolean;
static if(type == Type.Obj) return un.obj;
static if(type == Type.None) return this;
}
bool isType(Type type) const pure => this.type == type;
bool isType(Obj.Type type) const pure => this.type == Type.Obj && asObj.isType(type);
auto as(Type type)() const pure{
static if(type != Type.None)
assert(this.type == type);
static if(type == Type.Number) return un.number;
static if(type == Type.Bool) return un.boolean;
static if(type == Type.Obj) return un.obj;
static if(type == Type.None) return this;
}
bool isType(Obj.Type type) const pure => isType(Type.Obj) && asObj.isType(type);
bool asBoolean() const pure => as!(Type.Bool);
double asNumber() const pure => as!(Type.Number);
Obj* asObj() const pure => cast(Obj*)as!(Type.Obj);
static Value from(T: double)(T val) => Value(Type.Number, Un(number: val));
static Value from(T: bool)(T val) => Value(Type.Bool, Un(boolean: val));
static Value from(T)(T* val) if(__traits(compiles, val.obj)) => Value(Type.Obj, Un(obj: cast(Obj*)val));
static Value from(T: Value)(T val) => val;
static Value nil() pure => Value(Type.Nil);
bool isFalsey() const pure => (type == Type.Bool && asBoolean == false) || type == Type.Nil;
bool isFalsey() const pure => (isType(Type.Bool) && asBoolean == false) || isType(Type.Nil);
bool isTruthy() const pure => !isFalsey;
bool opEquals(Value rhs) const pure{
if(rhs.type != type)

View file

@ -108,7 +108,6 @@ struct VM{
Value readConstant() => chunk.constants[readVarUint()];
Obj.String* readString() => readConstant().asObj.asString;
bool checkBinaryType(alias type)() => peek(0).isType(type) && peek(1).isType(type);
bool checkSameType()() => peek(0).type == peek(1).type;
int binaryOp(string op, alias check, string checkMsg, alias pre)(){
if(!check){
runtimeError(checkMsg.ptr);
@ -234,7 +233,7 @@ struct VM{
}
static foreach(k, op; [ OpCode.Greater: ">", OpCode.Less: "<", OpCode.GreaterEqual: ">=", OpCode.LessEqual: "<=" ]){
case k:
if(binaryOp!(op, checkSameType, "Operands must be of the same type.", Value.Type.None))
if(binaryOp!(op, checkBinaryType!(Value.Type.Number), "Operands must be of the same type.", Value.Type.None))
return InterpretResult.RuntimeError;
break opSwitch;
}

View file

@ -20,6 +20,8 @@ int main(){
"./test/while.lox".match("1 2 3 2 1 0 1 1 2 3 2 1 0 1 ".replace(' ', '\n'));
"./test/fields.lox".match("0 10 ".replace(' ', '\n'));
"./test/nan!=nan.lox".match("false ".replace(' ', '\n'));
"./test/ops.lox".match("1\n2\n3\n4\n5\n6\n7\ntrue\nfalse\ntrue\ntrue\nhello, world\n");
"./test/shortcircuit.lox".match("true\nAAAA!\nAAAA!\nAAAA?\n");
"./test/closure.lox".match("1\n2\n");

4
test/nan!=nan.lox Normal file
View file

@ -0,0 +1,4 @@
var nan = 0 / 0;
print nan == nan;