Optimization 30
This commit is contained in:
parent
51bc1395f8
commit
f1e6ed4ef8
7 changed files with 101 additions and 29 deletions
3
dub.sdl
3
dub.sdl
|
|
@ -9,6 +9,7 @@ sourcePaths
|
||||||
configuration "clox" {
|
configuration "clox" {
|
||||||
buildOptions "betterC"
|
buildOptions "betterC"
|
||||||
sourcePaths "src/clox"
|
sourcePaths "src/clox"
|
||||||
|
versions "nanBoxing"
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration "clox-dbg" {
|
configuration "clox-dbg" {
|
||||||
|
|
@ -18,6 +19,8 @@ configuration "clox-dbg" {
|
||||||
sourcePaths "src/clox"
|
sourcePaths "src/clox"
|
||||||
buildRequirements "requireBoundsCheck" "requireContracts"
|
buildRequirements "requireBoundsCheck" "requireContracts"
|
||||||
|
|
||||||
|
versions "nanBoxing"
|
||||||
|
|
||||||
debugVersions "printCode" "traceExec"
|
debugVersions "printCode" "traceExec"
|
||||||
|
|
||||||
/* debugVersions "stressGC" */
|
/* debugVersions "stressGC" */
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ debug import std.stdio : writeln;
|
||||||
import clox.memory;
|
import clox.memory;
|
||||||
import clox.memorydbg;
|
import clox.memorydbg;
|
||||||
import clox.obj;
|
import clox.obj;
|
||||||
|
import clox.util;
|
||||||
import clox.value;
|
import clox.value;
|
||||||
import clox.container.dynarray;
|
import clox.container.dynarray;
|
||||||
import conf = clox.conf;
|
import conf = clox.conf;
|
||||||
|
|
@ -29,6 +30,7 @@ struct Table(K, V){
|
||||||
Entry[] pool;
|
Entry[] pool;
|
||||||
size_t alive;
|
size_t alive;
|
||||||
this(size_t size){
|
this(size_t size){
|
||||||
|
size = npow2(size);
|
||||||
pool = allocatePool(size);
|
pool = allocatePool(size);
|
||||||
}
|
}
|
||||||
void free(){
|
void free(){
|
||||||
|
|
@ -69,21 +71,21 @@ struct Table(K, V){
|
||||||
if(alive == 0)
|
if(alive == 0)
|
||||||
return null;
|
return null;
|
||||||
Hash hash = getHash(key);
|
Hash hash = getHash(key);
|
||||||
size_t index = hash % pool.length;
|
size_t index = hash & ((pool.length) - 1);
|
||||||
while(true){
|
while(true){
|
||||||
const entry = pool[index];
|
const entry = pool[index];
|
||||||
if(entry.isAlive && entry.key == key)
|
if(entry.isAlive && entry.key == key)
|
||||||
return &pool[index];
|
return &pool[index];
|
||||||
if(entry.isFree)
|
if(entry.isFree)
|
||||||
break;
|
break;
|
||||||
index = (index+1) % pool.length;
|
index = (index+1) & ((pool.length) - 1);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
void opIndexAssign(V val, in K key){
|
void opIndexAssign(V val, in K key){
|
||||||
checkResize();
|
checkResize();
|
||||||
Hash hash = getHash(key);
|
Hash hash = getHash(key);
|
||||||
size_t index = hash % pool.length;
|
size_t index = hash & ((pool.length) - 1);
|
||||||
while(true){
|
while(true){
|
||||||
const Entry* entry = &pool[index];
|
const Entry* entry = &pool[index];
|
||||||
if(entry.isAlive && entry.key == key)
|
if(entry.isAlive && entry.key == key)
|
||||||
|
|
@ -92,7 +94,7 @@ struct Table(K, V){
|
||||||
alive++;
|
alive++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
index = (index+1) % pool.length;
|
index = (index+1) & ((pool.length) - 1);
|
||||||
}
|
}
|
||||||
pool[index] = Entry(cast(K)key, val);
|
pool[index] = Entry(cast(K)key, val);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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";
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,41 +4,88 @@ import core.stdc.stdio;
|
||||||
|
|
||||||
import clox.memory;
|
import clox.memory;
|
||||||
import clox.obj;
|
import clox.obj;
|
||||||
|
import clox.util;
|
||||||
|
|
||||||
struct Value{
|
struct Value{
|
||||||
enum Type{
|
enum Type{
|
||||||
None, Bool, Nil, Number, Obj
|
None, Bool, Nil, Number, Obj
|
||||||
}
|
}
|
||||||
Type type;
|
version(nanBoxing){
|
||||||
private union Un{
|
private enum QNAN = ulong(0x7ffc000000000000);
|
||||||
bool boolean;
|
private enum SIGN_BIT = ulong(0x8000000000000000);
|
||||||
double number;
|
enum Tag{
|
||||||
Obj* obj;
|
Nil = 1, False = 2, True = 3
|
||||||
}
|
}
|
||||||
private Un un;
|
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);
|
bool asBoolean() const pure => as!(Type.Bool);
|
||||||
double asNumber() const pure => as!(Type.Number);
|
double asNumber() const pure => as!(Type.Number);
|
||||||
Obj* asObj() const pure => cast(Obj*)as!(Type.Obj);
|
Obj* asObj() const pure => cast(Obj*)as!(Type.Obj);
|
||||||
|
|
||||||
static Value from(T: double)(T val) => Value(Type.Number, Un(number: val));
|
bool isFalsey() const pure => (isType(Type.Bool) && asBoolean == false) || isType(Type.Nil);
|
||||||
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 isTruthy() const pure => !isFalsey;
|
bool isTruthy() const pure => !isFalsey;
|
||||||
bool opEquals(Value rhs) const pure{
|
bool opEquals(Value rhs) const pure{
|
||||||
if(rhs.type != type)
|
if(rhs.type != type)
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,6 @@ struct VM{
|
||||||
Value readConstant() => chunk.constants[readVarUint()];
|
Value readConstant() => chunk.constants[readVarUint()];
|
||||||
Obj.String* readString() => readConstant().asObj.asString;
|
Obj.String* readString() => readConstant().asObj.asString;
|
||||||
bool checkBinaryType(alias type)() => peek(0).isType(type) && peek(1).isType(type);
|
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)(){
|
int binaryOp(string op, alias check, string checkMsg, alias pre)(){
|
||||||
if(!check){
|
if(!check){
|
||||||
runtimeError(checkMsg.ptr);
|
runtimeError(checkMsg.ptr);
|
||||||
|
|
@ -234,7 +233,7 @@ struct VM{
|
||||||
}
|
}
|
||||||
static foreach(k, op; [ OpCode.Greater: ">", OpCode.Less: "<", OpCode.GreaterEqual: ">=", OpCode.LessEqual: "<=" ]){
|
static foreach(k, op; [ OpCode.Greater: ">", OpCode.Less: "<", OpCode.GreaterEqual: ">=", OpCode.LessEqual: "<=" ]){
|
||||||
case k:
|
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;
|
return InterpretResult.RuntimeError;
|
||||||
break opSwitch;
|
break opSwitch;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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/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/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/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/shortcircuit.lox".match("true\nAAAA!\nAAAA!\nAAAA?\n");
|
||||||
"./test/closure.lox".match("1\n2\n");
|
"./test/closure.lox".match("1\n2\n");
|
||||||
|
|
|
||||||
4
test/nan!=nan.lox
Normal file
4
test/nan!=nan.lox
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
var nan = 0 / 0;
|
||||||
|
print nan == nan;
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue