extensions
Steven Massey 5 years ago
parent bc04545aaf
commit c2cb12ae05

@ -0,0 +1,236 @@
/**********************************************************************
*
* Filename: crc.c
*
* Description: Slow and fast implementations of the CRC standards.
*
* Notes: The parameters for each supported CRC standard are
* defined in the header file crc.h. The implementations
* here should stand up to further additions to that list.
*
*
* Copyright (c) 2000 by Michael Barr. This software is placed into
* the public domain and may be used for any purpose. However, this
* notice must not be changed or removed and no warranty is either
* expressed or implied by its publication or distribution.
**********************************************************************/
#include "crc.h"
/*
* Derive parameters from the standard-specific parameters in crc.h.
*/
#define WIDTH (8 * sizeof(crc))
#define TOPBIT (1 << (WIDTH - 1))
#if (REFLECT_DATA == TRUE)
#undef REFLECT_DATA
#define REFLECT_DATA(X) ((unsigned char) reflect((X), 8))
#else
#undef REFLECT_DATA
#define REFLECT_DATA(X) (X)
#endif
#if (REFLECT_REMAINDER == TRUE)
#undef REFLECT_REMAINDER
#define REFLECT_REMAINDER(X) ((crc) reflect((X), WIDTH))
#else
#undef REFLECT_REMAINDER
#define REFLECT_REMAINDER(X) (X)
#endif
/*********************************************************************
*
* Function: reflect()
*
* Description: Reorder the bits of a binary sequence, by reflecting
* them about the middle position.
*
* Notes: No checking is done that nBits <= 32.
*
* Returns: The reflection of the original data.
*
*********************************************************************/
//static
unsigned long
reflect(unsigned long data, unsigned char nBits)
{
unsigned long reflection = 0x00000000;
unsigned char bit;
/*
* Reflect the data about the center bit.
*/
for (bit = 0; bit < nBits; ++bit)
{
/*
* If the LSB bit is set, set the reflection of it.
*/
if (data & 0x01)
{
reflection |= (1 << ((nBits - 1) - bit));
}
data = (data >> 1);
}
return (reflection);
} /* reflect() */
/*********************************************************************
*
* Function: crcSlow()
*
* Description: Compute the CRC of a given message.
*
* Notes:
*
* Returns: The CRC of the message.
*
*********************************************************************/
crc
crcSlow(unsigned char const message[], int nBytes)
{
crc remainder = INITIAL_REMAINDER;
int byte;
unsigned char bit;
/*
* Perform modulo-2 division, a byte at a time.
*/
for (byte = 0; byte < nBytes; ++byte)
{
/*
* Bring the next byte into the remainder.
*/
#if 1
remainder ^= (REFLECT_DATA (message [byte]) << (WIDTH - 8));
#endif
/*
* Perform modulo-2 division, a bit at a time.
*/
for (bit = 8; bit > 0; --bit)
{
/*
* Try to divide the current data bit.
*/
if (remainder & TOPBIT)
{
remainder = (remainder << 1) ^ POLYNOMIAL;
}
else
{
remainder = (remainder << 1);
}
}
}
/*
* The final remainder is the CRC result.
*/
return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE);
} /* crcSlow() */
crc crcTable[256];
/*********************************************************************
*
* Function: crcInit()
*
* Description: Populate the partial CRC lookup table.
*
* Notes: This function must be rerun any time the CRC standard
* is changed. If desired, it can be run "offline" and
* the table results stored in an embedded system's ROM.
*
* Returns: None defined.
*
*********************************************************************/
void
crcInit(void)
{
crc remainder;
int dividend;
unsigned char bit;
/*
* Compute the remainder of each possible dividend.
*/
for (dividend = 0; dividend < 256; ++dividend)
{
/*
* Start with the dividend followed by zeros.
*/
remainder = dividend << (WIDTH - 8);
/*
* Perform modulo-2 division, a bit at a time.
*/
for (bit = 8; bit > 0; --bit)
{
/*
* Try to divide the current data bit.
*/
if (remainder & TOPBIT)
{
remainder = (remainder << 1) ^ POLYNOMIAL;
}
else
{
remainder = (remainder << 1);
}
}
/*
* Store the result into the table.
*/
crcTable[dividend] = remainder;
}
} /* crcInit() */
/*********************************************************************
*
* Function: crcFast()
*
* Description: Compute the CRC of a given message.
*
* Notes: crcInit() must be called first.
*
* Returns: The CRC of the message.
*
*********************************************************************/
crc
crcFast(unsigned char const message[], int nBytes)
{
crc remainder = INITIAL_REMAINDER;
unsigned char data;
int byte;
/*
* Divide the message by the polynomial, a byte at a time.
*/
for (byte = 0; byte < nBytes; ++byte)
{
data = REFLECT_DATA(message[byte]) ^ (remainder >> (WIDTH - 8));
remainder = crcTable[data] ^ (remainder << 8);
}
/*
* The final remainder is the CRC.
*/
return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE);
} /* crcFast() */

@ -0,0 +1,80 @@
/**********************************************************************
*
* Filename: crc.h
*
* Description: A header file describing the various CRC standards.
*
* Notes:
*
*
* Copyright (c) 2000 by Michael Barr. This software is placed into
* the public domain and may be used for any purpose. However, this
* notice must not be changed or removed and no warranty is either
* expressed or implied by its publication or distribution.
**********************************************************************/
#ifndef _crc_h
#define _crc_h
#include <inttypes.h>
#define FALSE 0
#define TRUE !FALSE
/*
* Select the CRC standard from the list that follows.
*/
//#define CRC_CCITT
#if defined(CRC_CCITT)
typedef uint16_t crc;
#define CRC_NAME "CRC-CCITT"
#define POLYNOMIAL 0x1021
#define INITIAL_REMAINDER 0xFFFF
#define FINAL_XOR_VALUE 0x0000
#define REFLECT_DATA FALSE
#define REFLECT_REMAINDER FALSE
#define CHECK_VALUE 0x29B1
#elif defined(CRC16)
typedef uint16_t crc;
#define CRC_NAME "CRC-16"
#define POLYNOMIAL 0x8005
#define INITIAL_REMAINDER 0x0000
#define FINAL_XOR_VALUE 0x0000
#define REFLECT_DATA TRUE
#define REFLECT_REMAINDER TRUE
#define CHECK_VALUE 0xBB3D
#elif defined(CRC32)
typedef uint32_t crc;
#define CRC_NAME "CRC-32"
#define POLYNOMIAL 0x04C11DB7
#define INITIAL_REMAINDER 0xFFFFFFFF
#define FINAL_XOR_VALUE 0xFFFFFFFF
#define REFLECT_DATA TRUE
#define REFLECT_REMAINDER TRUE
#define CHECK_VALUE 0xCBF43926
#else
#error "One of CRC_CCITT, CRC16, or CRC32 must be #define'd."
#endif
void crcInit(void);
crc crcSlow(unsigned char const message[], int nBytes);
crc crcFast(unsigned char const message[], int nBytes);
unsigned long
reflect(unsigned long data, unsigned char nBits);
#endif /* _crc_h */

@ -0,0 +1,132 @@
#ifndef __EMSCRIPTEN__
# include <stdio.h>
# include <string.h>
# include <time.h>
# define m3Out_f64(OUT)
#else
#include "m3_host.h"
#endif
const int WIDTH = 1200;
const int HEIGHT = 800;
unsigned char image[WIDTH * HEIGHT * 3];
unsigned char colour(int iteration, int offset, int scale) {
iteration = ((iteration * scale) + offset) % 1024;
if (iteration < 256)
{
// return iteration;
}
else if (iteration < 512) {
iteration = 255 - (iteration - 255);
} else {
iteration = 0;
}
#ifndef __EMSCRIPTEN__
// printf ("colour: %d %d\n", iteration, (int) (unsigned char) iteration);
#endif
return iteration;
}
int iterateEquation(double x0, double y0, int maxiterations) {
#ifndef __EMSCRIPTEN__
// printf ("iterateEquation: %lf %lf %d", x0, y0, maxiterations);
#endif
double a = 0.0, b = 0.0, rx = 0.0, ry = 0.0;
int iterations = 0;
while (iterations < maxiterations && (rx * rx + ry * ry <= 4.0)) {
rx = a * a - b * b + x0;
ry = 2.0 * a * b + y0;
a = rx;
b = ry;
iterations++;
}
#ifndef __EMSCRIPTEN__
// printf (" -> %d\n", iterations);
#endif
return iterations;
}
// first pass: -0.743645 0.000293 120.000000 0.000000
double scale(double domainStart, double domainLength, double screenLength, double step)
{
double s = domainStart + domainLength * ((step - screenLength) / screenLength);
#ifndef __EMSCRIPTEN__
// printf ("scale: %lf %lf %lf %lf -> %lf\n", domainStart, domainLength, screenLength, step, s);
#endif
// m3Out_f64 (s);
return s;
}
void mandelbrot(int maxIterations, double cx, double cy, double diameter)
{
double verticalDiameter = diameter * HEIGHT / WIDTH;
for(double x = 0.0; x < WIDTH; x++) {
for(double y = 0.0; y < HEIGHT; y++) {
// map to mandelbrot coordinates
double rx = scale (cx, diameter, WIDTH, x);
double ry = scale (cy, verticalDiameter, HEIGHT, y);
int iterations = iterateEquation(rx, ry, maxIterations);
int idx = ((x + y * WIDTH) * 3);
// m3Out_i32 (idx);
// set the red and alpha components
image[idx] = iterations == maxIterations ? 0 : colour(iterations, 0, 4);
image[idx + 1] = iterations == maxIterations ? 0 : colour(iterations, 128, 4);
image[idx + 2] = iterations == maxIterations ? 0 : colour(iterations, 356, 4);
}
}
}
unsigned char* getImage() {
return &image[0];
}
#ifdef __EMSCRIPTEN__
#endif
int main() {
int numLoops = 10;
#ifdef __EMSCRIPTEN__
for (int i = 0; i < numLoops; i++)
{
mandelbrot(10000, -0.7436447860, 0.1318252536, 0.00029336);
}
m3Export (getImage (), WIDTH * HEIGHT * 3);
#else
clock_t start = clock() ;
for (int i = 0; i < numLoops; i++) {
mandelbrot(10000, -0.7436447860, 0.1318252536, 0.00029336);
}
clock_t end = clock() ;
double elapsed_time = (end-start)/(double)CLOCKS_PER_SEC ;
printf("%lf\n", elapsed_time);
FILE * f = fopen ("mandel.ppm", "wb");
const char * header = "P6\n1200 800\n255\n";
fwrite (header, 1, strlen (header), f);
fwrite (getImage (), 1, WIDTH * HEIGHT * 3, f);
fclose (f);
#endif
return 0;
}

@ -0,0 +1,66 @@
// emscripten compiler:
// emcc -s ERROR_ON_UNDEFINED_SYMBOLS=0 -O3 --profiling-funcs perf.crc.c crc.c -DCRC32 -I ../source/ -o crc.wasm
// gcc -O3 perf.crc.c crc.c -DCRC32 -I ../source/ -o crc
# ifdef __EMSCRIPTEN__
# include "m3_host.h"
# else
# include <stdio.h>
# endif
# include <string.h>
# include "crc.h"
// CRC32= f4b868ef / 4105726191
const char * string = "We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness.--That to secure these rights, Governments are instituted among Men, deriving their just powers from the consent of the governed, --That whenever any Form of Government becomes destructive of these ends, it is the Right of the People to alter or to abolish it, and to institute new Government, laying its foundation on such principles and organizing its powers in such form, as to them shall seem most likely to effect their Safety and Happiness. Prudence, indeed, will dictate that Governments long established should not be changed for light and transient causes; and accordingly all experience hath shewn, that mankind are more disposed to suffer, while evils are sufferable, than to right themselves by abolishing the forms to which they are accustomed. But when a long train of abuses and usurpations, pursuing invariably the same Object evinces a design to reduce them under absolute Despotism, it is their right, it is their duty, to throw off such Government, and to provide new Guards for their future security.--Such has been the patient sufferance of these Colonies; and such is now the necessity which constrains them to alter their former Systems of Government. The history of the present King of Great Britain is a history of repeated injuries and usurpations, all having in direct object the establishment of an absolute Tyranny over these States. To prove this, let Facts be submitted to a candid world.";
const char * ss = "123";
const char * z = "0";
size_t StrLen (const char * i_string)
{
const char * p = i_string;
while (* p != 0)
++p;
return p - i_string;
}
int main ()
{
// crcInit ();
uint32_t sum = 0;
for (int i = 0; i < 50000; ++i)
{
sum += crcSlow ((unsigned char *) string, StrLen (string));
}
// sum = reflect (87, 8);
// sum = StrLen (string);
// ;
// sum = reflect (0x12345678, 32);
// sum = strlen (string);
# ifndef __EMSCRIPTEN__
printf ("sum= %u %x\n", sum, sum);
#else
m3Out_i32 (sum);
# endif
return sum;
}

@ -0,0 +1,73 @@
//
// perf.dsp.c
// m3
//
// Created by Steven Massey on 4/30/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include <inttypes.h>
# ifdef __EMSCRIPTEN__
# include "m3_host.h"
# else
# include <stdio.h>
# endif
//class Noise
//{
// public:
//
// double Render ()
// {
// a ^= b;
// b += a;
//
// return scaler * b;
// }
//
// const double scaler = 1. / 0x7fffffffffffffff;
//
// int64_t a = 0x67452301, b = 0xefcdab89;
//};
typedef struct Noise
{
int64_t a, b;
}
Noise;
double Noise_Render (Noise * i_noise)
{
const double scaler = 1. / 0x7fffffffffffffff;
i_noise->a ^= i_noise->b;
i_noise->b += i_noise->a;
return scaler * i_noise->a;
}
int main ()
{
Noise noise = { 0x67452301, 0xefcdab89 };
const int s = 1000000000;
// double * a = new double [s];
double a;
for (int i = 0; i < s; ++i)
{
a = Noise_Render (& noise);
// m3Export (& a, 0);
// a = noise.Render ();
}
#ifdef __EMSCRIPTEN__
m3Export (& a, 0);
#else
printf ("\n%lf\n", a);
#endif
}

Binary file not shown.

Binary file not shown.

@ -0,0 +1,3 @@
source ../../../emsdk/emsdk_env.sh
emcc -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s SIDE_MODULE=1 -O1 --profiling-funcs -I ./zlib/ -I ../../source/ ./zlib/compress.c ./zlib/deflate.c ./zlib/crc32.c ./zlib/adler32.c \
./zlib/trees.c ./zlib/zutil.c test.zlib.c -o zlib.wasm

@ -0,0 +1,47 @@
//
// test.zlib.c
// m3
//
// Created by Steven Massey on 6/28/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include <stdlib.h>
#include <stdio.h>
#include "m3_host.h"
#include "zlib.h"
int main ()
{
int srcLength = 804335;
void * data = malloc (804335);
uLongf length = 900000;
void * dest = malloc (length);
FILE * f = fopen ("/Users/smassey/98-0.txt", "r+b");
size_t s = 0;
if (f)
{
size_t d = fread (data, 1, srcLength, f);
// ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
// const Bytef *source, uLong sourceLen,
// int level));
s = compress2 (dest, & length, data, srcLength, 9);
#ifdef __EMSCRIPTEN__
m3Out_i32 (length);
m3Out_i32 (s);
#else
printf ("%d -> %d result: %d\n", d, length, s);
#endif
}
free (data);
return s;
}

@ -0,0 +1,371 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 48;
objects = {
/* Begin PBXBuildFile section */
B522710322769400003D2ACE /* m3_host.c in Sources */ = {isa = PBXBuildFile; fileRef = B5227101227693FF003D2ACE /* m3_host.c */; };
B54326322267FE450079BFE8 /* m3_exec.c in Sources */ = {isa = PBXBuildFile; fileRef = B54326312267FE450079BFE8 /* m3_exec.c */; };
B54326352267FF7E0079BFE8 /* m3_compile.c in Sources */ = {isa = PBXBuildFile; fileRef = B54326342267FF7E0079BFE8 /* m3_compile.c */; };
B5432638226A72AA0079BFE8 /* m3_code.c in Sources */ = {isa = PBXBuildFile; fileRef = B5432637226A72AA0079BFE8 /* m3_code.c */; };
B543263B226A76120079BFE8 /* m3_env.c in Sources */ = {isa = PBXBuildFile; fileRef = B543263A226A76120079BFE8 /* m3_env.c */; };
B543263E226AE2B60079BFE8 /* m3_parse.c in Sources */ = {isa = PBXBuildFile; fileRef = B543263D226AE2B60079BFE8 /* m3_parse.c */; };
B5537E52227709B300DD3E2A /* m3_bind.c in Sources */ = {isa = PBXBuildFile; fileRef = B5537E51227709B300DD3E2A /* m3_bind.c */; };
B5665E3022D577A8003F799A /* m3_emit.c in Sources */ = {isa = PBXBuildFile; fileRef = B5665E2F22D577A8003F799A /* m3_emit.c */; };
B56FD4C22282A49C006B249B /* m3_module.c in Sources */ = {isa = PBXBuildFile; fileRef = B56FD4C12282A49C006B249B /* m3_module.c */; };
B5946A0422654C05008572B8 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5946A0322654C05008572B8 /* main.cpp */; };
B5EE06AB2266EF3900DCF003 /* m3_core.c in Sources */ = {isa = PBXBuildFile; fileRef = B5EE06AA2266EF3800DCF003 /* m3_core.c */; };
B5F7788A2275781B001A4F1E /* m3_optimize.c in Sources */ = {isa = PBXBuildFile; fileRef = B5F778892275781B001A4F1E /* m3_optimize.c */; };
B5F7788D227579A9001A4F1E /* m3_info.c in Sources */ = {isa = PBXBuildFile; fileRef = B5F7788C227579A9001A4F1E /* m3_info.c */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
B59469F622654B8F008572B8 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
B50D654E226D117100C09526 /* m3.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = m3.h; path = ../../source/m3.h; sourceTree = "<group>"; };
B51327812277EB6000447182 /* perf.crc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = perf.crc.c; path = ../../benchmark/perf.crc.c; sourceTree = "<group>"; };
B51D6D5622C69EF2008BA3E0 /* test.zlib.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = test.zlib.c; path = ../../benchmark/zlib/test.zlib.c; sourceTree = "<group>"; };
B5227101227693FF003D2ACE /* m3_host.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = m3_host.c; path = ../../source/m3_host.c; sourceTree = "<group>"; };
B5227102227693FF003D2ACE /* m3_host.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = m3_host.h; path = ../../source/m3_host.h; sourceTree = "<group>"; };
B539F35522D2B68500219799 /* m3_module.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = m3_module.h; path = ../../source/m3_module.h; sourceTree = "<group>"; };
B54326302267FE450079BFE8 /* m3_exec.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = m3_exec.h; path = ../../source/m3_exec.h; sourceTree = "<group>"; };
B54326312267FE450079BFE8 /* m3_exec.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = m3_exec.c; path = ../../source/m3_exec.c; sourceTree = "<group>"; };
B54326332267FF7E0079BFE8 /* m3_compile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = m3_compile.h; path = ../../source/m3_compile.h; sourceTree = "<group>"; };
B54326342267FF7E0079BFE8 /* m3_compile.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = m3_compile.c; path = ../../source/m3_compile.c; sourceTree = "<group>"; };
B5432636226A72AA0079BFE8 /* m3_code.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = m3_code.h; path = ../../source/m3_code.h; sourceTree = "<group>"; };
B5432637226A72AA0079BFE8 /* m3_code.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = m3_code.c; path = ../../source/m3_code.c; sourceTree = "<group>"; };
B5432639226A76120079BFE8 /* m3_env.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = m3_env.h; path = ../../source/m3_env.h; sourceTree = "<group>"; };
B543263A226A76120079BFE8 /* m3_env.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = m3_env.c; path = ../../source/m3_env.c; sourceTree = "<group>"; };
B543263D226AE2B60079BFE8 /* m3_parse.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = m3_parse.c; path = ../../source/m3_parse.c; sourceTree = "<group>"; };
B54CAEEE22D050DE00BA35D1 /* m3_exception.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = m3_exception.h; path = ../../source/m3_exception.h; sourceTree = "<group>"; };
B5537E51227709B300DD3E2A /* m3_bind.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = m3_bind.c; path = ../../source/m3_bind.c; sourceTree = "<group>"; };
B5541C6A227E21C000ADC074 /* m3_config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = m3_config.h; path = ../../source/m3_config.h; sourceTree = "<group>"; };
B5638198226FD06F00B00459 /* m3.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = m3.hpp; path = ../../source/m3.hpp; sourceTree = "<group>"; };
B5665E2E22D577A8003F799A /* m3_emit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = m3_emit.h; path = ../../source/m3_emit.h; sourceTree = "<group>"; };
B5665E2F22D577A8003F799A /* m3_emit.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = m3_emit.c; path = ../../source/m3_emit.c; sourceTree = "<group>"; };
B56FD4C12282A49C006B249B /* m3_module.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = m3_module.c; path = ../../source/m3_module.c; sourceTree = "<group>"; };
B59469F822654B8F008572B8 /* m3 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = m3; sourceTree = BUILT_PRODUCTS_DIR; };
B5946A0322654C05008572B8 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = ../../source/main.cpp; sourceTree = "<group>"; };
B5BE67F42280DCCC00D5CA2A /* test.trap.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = test.trap.c; path = ../../test/test.trap.c; sourceTree = "<group>"; };
B5C4BE4522790CE400CEB4AB /* mandelbrot.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mandelbrot.c; path = ../../benchmark/mandelbrot.c; sourceTree = "<group>"; };
B5C4BE4722790FE200CEB4AB /* perf.dsp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = perf.dsp.c; path = ../../benchmark/perf.dsp.c; sourceTree = "<group>"; };
B5D5F5FA2277B12100FEA827 /* output.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = output.c; path = ../../test/output.c; sourceTree = "<group>"; };
B5EE06AA2266EF3800DCF003 /* m3_core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = m3_core.c; path = ../../source/m3_core.c; sourceTree = "<group>"; };
B5EE06AC2266EF7100DCF003 /* m3_core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = m3_core.h; path = ../../source/m3_core.h; sourceTree = "<group>"; };
B5EFD6DD228E49FE001965C7 /* test.host.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = test.host.c; path = ../../test/test.host.c; sourceTree = "<group>"; };
B5F778892275781B001A4F1E /* m3_optimize.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = m3_optimize.c; path = ../../source/m3_optimize.c; sourceTree = "<group>"; };
B5F7788C227579A9001A4F1E /* m3_info.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = m3_info.c; path = ../../source/m3_info.c; sourceTree = "<group>"; };
B5FD6505227A53110077AAA3 /* m3_exec_defs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = m3_exec_defs.h; path = ../../source/m3_exec_defs.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B59469F522654B8F008572B8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B59469EF22654B8F008572B8 = {
isa = PBXGroup;
children = (
B5D5F5F92277B11300FEA827 /* tests */,
B5946A0222654BE0008572B8 /* m3 */,
);
sourceTree = "<group>";
};
B59469F922654B8F008572B8 /* Products */ = {
isa = PBXGroup;
children = (
B59469F822654B8F008572B8 /* m3 */,
);
name = Products;
sourceTree = "<group>";
};
B5946A0222654BE0008572B8 /* m3 */ = {
isa = PBXGroup;
children = (
B5541C6A227E21C000ADC074 /* m3_config.h */,
B50D654E226D117100C09526 /* m3.h */,
B5638198226FD06F00B00459 /* m3.hpp */,
B5EE06AC2266EF7100DCF003 /* m3_core.h */,
B5EE06AA2266EF3800DCF003 /* m3_core.c */,
B56FD4C12282A49C006B249B /* m3_module.c */,
B539F35522D2B68500219799 /* m3_module.h */,
B5432639226A76120079BFE8 /* m3_env.h */,
B543263A226A76120079BFE8 /* m3_env.c */,
B5FD6505227A53110077AAA3 /* m3_exec_defs.h */,
B54326302267FE450079BFE8 /* m3_exec.h */,
B54326312267FE450079BFE8 /* m3_exec.c */,
B5665E2E22D577A8003F799A /* m3_emit.h */,
B5665E2F22D577A8003F799A /* m3_emit.c */,
B54326332267FF7E0079BFE8 /* m3_compile.h */,
B54326342267FF7E0079BFE8 /* m3_compile.c */,
B5F778892275781B001A4F1E /* m3_optimize.c */,
B5432636226A72AA0079BFE8 /* m3_code.h */,
B5432637226A72AA0079BFE8 /* m3_code.c */,
B543263D226AE2B60079BFE8 /* m3_parse.c */,
B5537E51227709B300DD3E2A /* m3_bind.c */,
B5227101227693FF003D2ACE /* m3_host.c */,
B5227102227693FF003D2ACE /* m3_host.h */,
B5F7788C227579A9001A4F1E /* m3_info.c */,
B54CAEEE22D050DE00BA35D1 /* m3_exception.h */,
B59469F922654B8F008572B8 /* Products */,
B5946A0322654C05008572B8 /* main.cpp */,
);
name = m3;
sourceTree = "<group>";
};
B5D5F5F92277B11300FEA827 /* tests */ = {
isa = PBXGroup;
children = (
B51327812277EB6000447182 /* perf.crc.c */,
B5D5F5FA2277B12100FEA827 /* output.c */,
B5C4BE4522790CE400CEB4AB /* mandelbrot.c */,
B5C4BE4722790FE200CEB4AB /* perf.dsp.c */,
B5BE67F42280DCCC00D5CA2A /* test.trap.c */,
B5EFD6DD228E49FE001965C7 /* test.host.c */,
B51D6D5622C69EF2008BA3E0 /* test.zlib.c */,
);
name = tests;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
B59469F722654B8F008572B8 /* m3 */ = {
isa = PBXNativeTarget;
buildConfigurationList = B59469FF22654B8F008572B8 /* Build configuration list for PBXNativeTarget "m3" */;
buildPhases = (
B59469F422654B8F008572B8 /* Sources */,
B59469F522654B8F008572B8 /* Frameworks */,
B59469F622654B8F008572B8 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = m3;
productName = m3;
productReference = B59469F822654B8F008572B8 /* m3 */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
B59469F022654B8F008572B8 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0920;
ORGANIZATIONNAME = "Steven Massey";
TargetAttributes = {
B59469F722654B8F008572B8 = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = B59469F322654B8F008572B8 /* Build configuration list for PBXProject "m3" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = B59469EF22654B8F008572B8;
productRefGroup = B59469F922654B8F008572B8 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
B59469F722654B8F008572B8 /* m3 */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
B59469F422654B8F008572B8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B543263E226AE2B60079BFE8 /* m3_parse.c in Sources */,
B522710322769400003D2ACE /* m3_host.c in Sources */,
B5537E52227709B300DD3E2A /* m3_bind.c in Sources */,
B5946A0422654C05008572B8 /* main.cpp in Sources */,
B5432638226A72AA0079BFE8 /* m3_code.c in Sources */,
B54326352267FF7E0079BFE8 /* m3_compile.c in Sources */,
B543263B226A76120079BFE8 /* m3_env.c in Sources */,
B54326322267FE450079BFE8 /* m3_exec.c in Sources */,
B5F7788A2275781B001A4F1E /* m3_optimize.c in Sources */,
B56FD4C22282A49C006B249B /* m3_module.c in Sources */,
B5F7788D227579A9001A4F1E /* m3_info.c in Sources */,
B5665E3022D577A8003F799A /* m3_emit.c in Sources */,
B5EE06AB2266EF3900DCF003 /* m3_core.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
B59469FD22654B8F008572B8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
B59469FE22654B8F008572B8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
B5946A0022654B8F008572B8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
B5946A0122654B8F008572B8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
GCC_OPTIMIZATION_LEVEL = fast;
OTHER_CFLAGS = "-fomit-frame-pointer";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
B59469F322654B8F008572B8 /* Build configuration list for PBXProject "m3" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B59469FD22654B8F008572B8 /* Debug */,
B59469FE22654B8F008572B8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B59469FF22654B8F008572B8 /* Build configuration list for PBXNativeTarget "m3" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B5946A0022654B8F008572B8 /* Debug */,
B5946A0122654B8F008572B8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = B59469F022654B8F008572B8 /* Project object */;
}

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:m3.xcodeproj">
</FileRef>
</Workspace>

@ -0,0 +1,248 @@
//
// m3.h
//
// M3 / Massey Meta Machine: a WebAssembly interpreter
//
// Created by Steven Massey on 4/21/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
/*
NOTES:
- define d_m3LogOutput=1 to see printf log info
FIX:
- function types need to move to the runtime structure so that all modules can share types
then type equality can be a simple pointer compare for indirect call checks
TODO:
- assumes little-endian CPU
- needs work for a 32-bit architecture
- e.g. m3 code stream should be 32-bit aligned, but still needs to handle 64-bit constants
POSSIBLE FUTURE FEATURES:
- segmented stack
- M3 stack that lives on the C stack (this might be useful in a memory constrained environment).
*/
#ifndef m3_h
#define m3_h
#include <inttypes.h>
typedef const char * M3Result;
struct M3Runtime; typedef struct M3Runtime * IM3Runtime;
struct M3Module; typedef struct M3Module * IM3Module;
struct M3Function; typedef struct M3Function * IM3Function;
typedef struct M3ErrorInfo
{
M3Result result;
IM3Runtime runtime;
IM3Module module;
IM3Function function;
// compilation constants
const char * file;
uint32_t line;
char message [1024];
}
M3ErrorInfo;
enum // EWaTypes
{
c_m3Type_none = 0,
c_m3Type_i32 = 1,
c_m3Type_i64 = 2,
c_m3Type_f32 = 3,
c_m3Type_f64 = 4,
c_m3Type_void = 5,
c_m3Type_ptr = 6,
c_m3Type_module
};
typedef struct M3ImportInfo
{
const char * moduleUtf8;
const char * fieldUtf8;
// unsigned char type;
}
M3ImportInfo;
typedef M3ImportInfo * IM3ImportInfo;
# ifndef d_m3ErrorConst
# define d_m3ErrorConst(LABEL, STRING) extern const M3Result c_m3Err_##LABEL;
# endif
// -------------------------------------------------------------------------------------------------------------------------------
// error codes
// -------------------------------------------------------------------------------------------------------------------------------
d_m3ErrorConst (none, NULL)
// general errors
d_m3ErrorConst (typeListOverflow, "type list count exceeds 32 types")
d_m3ErrorConst (mallocFailed, "memory allocation failed")
// parse errors
d_m3ErrorConst (incompatibleWasmVersion, "incompatible WASM binary version")
d_m3ErrorConst (wasmMalformed, "malformed WASM binary")
d_m3ErrorConst (misorderedWasmSection, "out of ordr WASM section")
d_m3ErrorConst (wasmUnderrun, "underrun while parsing WASM binary")
d_m3ErrorConst (wasmOverrun, "overrun while parsing WASM binary")
d_m3ErrorConst (wasmMissingInitExpr, "missing init_expr in WASM binary")
d_m3ErrorConst (lebOverflow, "LEB encoded value overflow")
d_m3ErrorConst (missingUTF8, "zero length UTF-8 string")
d_m3ErrorConst (wasmSectionUnderrun, "section underrun while parsing WASM binary")
d_m3ErrorConst (wasmSectionOverrun, "section overrun while parsing WASM binary")
d_m3ErrorConst (invalidTypeId, "unknown value_type")
// link errors
d_m3ErrorConst (moduleAlreadyLinked, "attempting to bind module to multiple runtimes")
d_m3ErrorConst (functionLookupFailed, "function lookup failed")
d_m3ErrorConst (functionImportMissing, "missing imported function");
// compilation errors
d_m3ErrorConst (noCompiler, "no compiler found for opcode")
d_m3ErrorConst (unknownOpcode, "unknown opcode")
d_m3ErrorConst (functionStackOverflow, "compiling function overran its stack height limit")
d_m3ErrorConst (functionStackUnderrun, "compiling function underran the stack")
d_m3ErrorConst (mallocFailedCodePage, "memory allocation failed when acquiring a new M3 code page")
d_m3ErrorConst (optimizerFailed, "optimizer failed") // not a fatal error. a result,
// runtime errors
d_m3ErrorConst (missingCompiledCode, "function is missing compiled m3 code")
d_m3ErrorConst (wasmMemoryOverflow, "runtime ran out of memory")
d_m3ErrorConst (globalMemoryNotAllocated, "global memory is missing from a module")
d_m3ErrorConst (globaIndexOutOfBounds, "global index is too large")
// traps
d_m3ErrorConst (trapTruncationOverflow, "[trap] truncation from floating point to integer overflow")
d_m3ErrorConst (trapOutOfBoundsMemoryAccess, "[trap] out of bounds memory access")
d_m3ErrorConst (trapDivisionByZero, "[trap] division by zero")
d_m3ErrorConst (trapRemainderByZero, "[trap] remainder by zero")
d_m3ErrorConst (trapTableIndexOutOfRange, "[trap] table index is out of range")
d_m3ErrorConst (runtimeTrap, "[trap] unspecified runtime trap")
typedef void (* M3Free) (const void * i_data, void * i_ref);
typedef void (* M3Importer) (IM3ImportInfo io_import, IM3Module io_module, void * i_ref);
typedef int64_t (* M3Callback) (IM3Function i_currentFunction, void * i_ref);
//-------------------------------------------------------------------------------------------------------------------------------
// configuration (found in m3_core.h)
//-------------------------------------------------------------------------------------------------------------------------------
/*
define default
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
d_m3AlignWasmMemoryToPages false The WebAssembly spec defines a 64kB page size and memory size is always
quantized to pages. In a non-Javascript centric WA host this seems
unnecessary.
d_m3CodePageSize 4096 WebAssembly is compiled to M3 code pages of this size.
*/
//--------------------------------------------------------------------------------------------------------------------------------------------
// "global" environment
//--------------------------------------------------------------------------------------------------------------------------------------------
// IM3Environment m3_NewEnvironment
//--------------------------------------------------------------------------------------------------------------------------------------------
// execution context
//--------------------------------------------------------------------------------------------------------------------------------------------
IM3Runtime m3_NewRuntime (uint32_t i_stackSizeInBytes);
M3Result m3_RegisterFunction (IM3Runtime io_runtime,
const char * const i_functionName,
const char * const i_signature,
const void * const i_function /* , const void * const i_ref */);
void m3_FreeRuntime (IM3Runtime i_runtime);
// void m3_SetImporter (IM3Runtime i_runtime, M3Importer i_importHandler);
// void m3_SetTimeoutHandler (IM3Runtime i_runtime, float i_periodInSeconds, M3Callback i_callback);
//--------------------------------------------------------------------------------------------------------------------------------------------
// modules
//--------------------------------------------------------------------------------------------------------------------------------------------
M3Result m3_ParseModule (IM3Module * o_module,
const uint8_t * const i_wasmBytes,
uint32_t i_numWasmBytes
// M3Free i_releasHandler // i_ref argument type provided to M3Free() handler is <IM3Module>
);
// If i_wasmReleaseHandler is provided, then i_wasmBytes must be persistent until the handler is invoked.
// If the handler is NULL, ParseModule will make a copy of i_wasmBytes and releases ownership of the pointer.
// if a result is return, * o_module will be set to NULL
void m3_FreeModule (IM3Module i_module);
// Only unloaded modules need to be freed.
// M3Result m3_EnableOptimizer (IM3Module io_module, bool i_enable);
M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module);
// LoadModule transfers ownership of a module to the runtime. Do not free modules once imported into the runtime.
M3Result m3_LinkFunction (IM3Module io_module,
const char * const i_functionName,
const char * const i_signature,
const void * const i_function /* , const void * const i_ref */);
// signature is null terminated
// M3Result m3_SetGlobal
//--------------------------------------------------------------------------------------------------------------------------------------------
// functions
//--------------------------------------------------------------------------------------------------------------------------------------------
M3Result m3_FindFunction (IM3Function * o_function,
IM3Runtime i_runtime,
const char * const i_functionName);
// M3Result m3_GetCFunction (void ** o_cFunction, IM3Runtime i_runtime,
// const char * const i_functionName, const char * const i_signature);
M3Result m3_Call (IM3Function i_function);
// void * /* return */ m3_Call (IM3Function i_function, M3Result * o_result);
// IM3Functions are valid during the lifetime of the originating runtime
M3ErrorInfo m3_GetErrorInfo (IM3Runtime i_runtime);
//--------------------------------------------------------------------------------------------------------------------------------------------
// debug info
//--------------------------------------------------------------------------------------------------------------------------------------------
void m3_PrintRuntimeInfo (IM3Runtime i_runtime);
void m3_PrintM3Info (void);
void m3_PrintProfilerInfo (void);
#endif /* m3_h */

@ -0,0 +1,18 @@
//
// m3.hpp
//
// M3/Massey Meta Machine: a WebAssembly interpreter
//
// Created by Steven Massey on 4/23/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef m3_hpp
#define m3_hpp
extern "C"
{
# include "m3.h"
}
#endif /* m3_hpp */

@ -0,0 +1,315 @@
//
// m3_bind.c
// m3
//
// Created by Steven Massey on 4/29/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include <assert.h>
#include "m3_exec.h"
#include "m3_env.h"
typedef struct M3State
{
pc_t pc;
m3stack_t sp;
u8 * mem;
}
M3State;
// This binding code only work'n for System V AMD64 ABI calling convention (macOS & Linux)
// Needs work for MS cdecl
#define d_m3BindingArgList i64 _i0, i64 _i1, i64 _i2, i64 _i3, f64 _f0, f64 _f1, f64 _f2, f64 _f3
#define d_m3BindingArgs _i0, _i1, _i2, _i3, _f0, _f1, _f2, _f3
typedef m3ret_t (* M3ArgPusher) (d_m3BindingArgList, M3State * i_state);
typedef f64 (* M3ArgPusherFpReturn) (d_m3BindingArgList, M3State * i_state);
m3ret_t PushArg_module (d_m3BindingArgList, M3State * _state)
{
void ** ptr = (void **) _state->mem;
IM3Module module = * (ptr - 2);
_i0 = (i64) module;
M3ArgPusher pusher = (* _state->pc++);
return pusher (d_m3BindingArgs, _state);
}
//printf ("push ptr: r%d off: %d\n", INDEX, offset);
#define d_argPusherPointer(INDEX) \
m3ret_t PushArg_p##INDEX (d_m3BindingArgList, M3State * _state) \
{ \
i32 offset = (u32) * (_state->sp++); \
_i##INDEX = (i64) (_state->mem + offset); \
M3ArgPusher pusher = (* _state->pc++); \
return pusher (d_m3BindingArgs, _state); \
}
//printf ("push [%d]: %lld\n", INDEX, _i##INDEX);
#define d_argPusherInt(INDEX) \
m3ret_t PushArg_i##INDEX (d_m3BindingArgList, M3State * _state) \
{ \
_i##INDEX = * (_state->sp++); \
M3ArgPusher pusher = (* _state->pc++); \
return pusher (d_m3BindingArgs, _state); \
}
// printf ("push [%d]: %lf\n", INDEX, * (TYPE *) (_state->sp)); \
#define d_argPusherFloat(INDEX,TYPE) \
m3ret_t PushArg_##TYPE##_##INDEX (d_m3BindingArgList, M3State * _state) \
{ \
_f##INDEX = * (TYPE *) (_state->sp++); \
M3ArgPusher pusher = (* _state->pc++); \
return pusher (d_m3BindingArgs, _state); \
}
d_argPusherPointer (0) d_argPusherPointer (1) d_argPusherPointer (2) d_argPusherPointer (3)
d_argPusherInt (0) d_argPusherInt (1) d_argPusherInt (2) d_argPusherInt (3)
d_argPusherFloat (0, f32) d_argPusherFloat (1, f32) d_argPusherFloat (2, f32) d_argPusherFloat (3, f32)
d_argPusherFloat (0, f64) d_argPusherFloat (1, f64) d_argPusherFloat (2, f64) d_argPusherFloat (3, f64)
M3ArgPusher c_m3PointerPushers [] = { PushArg_p0, PushArg_p1, PushArg_p2, PushArg_p3, NULL }; // one dummy is required
M3ArgPusher c_m3IntPushers [] = { PushArg_i0, PushArg_i1, PushArg_i2, PushArg_i3, NULL };
M3ArgPusher c_m3Float32Pushers [] = { PushArg_f32_0, PushArg_f32_1, PushArg_f32_2, PushArg_f32_3, NULL };
M3ArgPusher c_m3Float64Pushers [] = { PushArg_f64_0, PushArg_f64_1, PushArg_f64_2, PushArg_f64_3, NULL };
d_m3RetSig CallCFunction_i64 (d_m3OpSig)
{
M3ArgPusher pusher = (M3ArgPusher) (* _pc++);
M3State state = { _pc, _sp, _mem };
i64 r = (i64) pusher (0, 0, 0, 0, 0., 0., 0., 0., & state);
* _sp = r;
return 0;
}
d_m3RetSig CallCFunction_f64 (d_m3OpSig)
{
M3ArgPusherFpReturn pusher = (M3ArgPusherFpReturn) (* _pc++);
M3State state = { _pc, _sp, _mem };
f64 r = (f64) pusher (0, 0, 0, 0, 0., 0., 0., 0., & state);
* (f64 *) (_sp) = r;
return 0;
}
d_m3RetSig CallCFunction_f32 (d_m3OpSig)
{
M3ArgPusherFpReturn pusher = (M3ArgPusherFpReturn) (* _pc++);
M3State state = { _pc, _sp, _mem };
f32 r = (f32) pusher (0, 0, 0, 0, 0., 0., 0., 0., & state);
* (f32 *) (_sp) = r;
return 0;
}
d_m3RetSig CallCFunction_ptr (d_m3OpSig)
{
M3ArgPusher pusher = (M3ArgPusher) (* _pc++);
M3State state = { _pc, _sp, _mem };
const u8 * r = pusher (0, 0, 0, 0, 0., 0., 0., 0., & state);
void ** ptr = (void **) _mem;
IM3Module module = * (ptr - 2);
size_t offset = r - (const u8 *) module->memory.wasmPages;
* (i32 *) (_sp) = (i32) offset;
return 0;
}
u8 ConvertTypeCharToTypeId (char i_code)
{
u8 type = 0;
if (i_code > c_m3Type_ptr)
{
if (i_code == 'v') type = c_m3Type_void;
else if (i_code == '*') type = c_m3Type_ptr;
else if (i_code == '8') type = c_m3Type_i32;
else if (i_code == 'f') type = c_m3Type_f32;
else if (i_code == 'F') type = c_m3Type_f64;
else if (i_code == 'i') type = c_m3Type_i32;
else if (i_code == 'I') type = c_m3Type_i64;
else if (i_code == 'M') type = c_m3Type_module;
}
return type;
}
M3Result ValidateSignature (IM3Function i_function, u8 * o_normalizedSignature, ccstr_t i_linkingSignature)
{
M3Result result = c_m3Err_none;
cstr_t sig = i_linkingSignature;
char returnTypeChar = * sig++;
if (returnTypeChar)
{
u8 returnType = ConvertTypeCharToTypeId (returnTypeChar);
if (returnType)
{
o_normalizedSignature [0] = returnType;
u32 hasModuleArgument = 0;
u32 i = 0;
while (i < c_m3MaxNumFunctionArgs)
{
char typeChar = * sig++;
if (typeChar)
{
if (typeChar == '(' or typeChar == ' ') // allow some decoration
continue;
else if (typeChar == ')')
break;
u8 type = ConvertTypeCharToTypeId (typeChar);
if (type)
{
if (type == c_m3Type_module)
hasModuleArgument = 1;
// FIX: compare to fun; need to ignore module argument
o_normalizedSignature [++i] = type;
}
else
{
result = "unknown type char";
break;
}
}
else break;
}
if (* sig == 0)
{
if (i == GetFunctionNumArgs (i_function) + hasModuleArgument)
{
}
else result = "function arg count mismatch";
}
else result = "arg count overflow";
}
else result = "invalid return type char code";
}
else result = "missing return type";
return result;
}
M3Result m3_RegisterFunction (IM3Runtime io_runtime, const char * const i_functionName, const char * const i_signature, const void * const i_function /* , const void * const i_ref */)
{
M3Result result = c_m3Err_none;
return result;
}
M3Result m3_LinkFunction (IM3Module io_module, ccstr_t i_functionName, ccstr_t i_signature, const void * i_function)
{
M3Result result = c_m3Err_none;
M3ArgPusher pushers [c_m3MaxNumFunctionArgs + 1] = {};
u8 signature [1 /* return */ + c_m3MaxNumFunctionArgs + 2] = {};
IM3Function func = (IM3Function) v_FindFunction (io_module, i_functionName);
if (func)
{
result = ValidateSignature (func, signature, i_signature);
if (not result)
{
u32 intIndex = 0;
u32 floatIndex = 0;
u8 * sig = & signature [1];
u32 i = 0;
while (* sig)
{
M3ArgPusher * pusher = & pushers [i];
u8 type = * sig;
if (type == c_m3Type_ptr) * pusher = c_m3PointerPushers [intIndex++];
else if (IsIntType (type)) * pusher = c_m3IntPushers [intIndex++];
else if (type == c_m3Type_f32) * pusher = c_m3Float32Pushers [floatIndex++];
else if (type == c_m3Type_f64) * pusher = c_m3Float64Pushers [floatIndex++];
else if (type == c_m3Type_module)
{
* pusher = PushArg_module;
d_m3Assert (i == 0); // can only push to arg0
++intIndex;
}
else
{
result = "FIX";
abort ();
}
++i; ++sig;
}
if (not result)
{
IM3CodePage page = AcquireCodePageWithCapacity (io_module->runtime, /*setup-func:*/ 1 + /*arg pushers:*/ i + /*target c-function:*/ 1);
func->compiled = GetPagePC (page);
func->module = io_module;
IM3Operation callerOp = CallCFunction_i64;
u8 returnType = signature [0];
if (returnType == c_m3Type_f64)
callerOp = CallCFunction_f64;
else if (returnType == c_m3Type_f32)
callerOp = CallCFunction_f32;
else if (returnType == c_m3Type_ptr)
callerOp = CallCFunction_ptr;
EmitWord (page, callerOp);
for (i32 j = 0; j < i; ++j)
EmitWord (page, pushers [j]);
EmitWord (page, i_function);
ReleaseCodePage (io_module->runtime, page);
}
}
}
else result = c_m3Err_functionLookupFailed;
return result;
}

@ -0,0 +1,103 @@
//
// m3_code.c
// M3: Massey Meta Machine
//
// Created by Steven Massey on 4/19/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include "m3_code.h"
#include <assert.h>
IM3CodePage NewCodePage (u32 i_minNumLines)
{
static u32 s_sequence = 0;
u32 pageSize = sizeof (M3CodePageHeader) + sizeof (code_t) * i_minNumLines;
pageSize = (pageSize + 4095) & ~4095; // align to 4kB
IM3CodePage page;
m3Malloc ((void **) & page, pageSize);
if (page)
{
page->info.sequence = ++s_sequence; m3log (code, "new page: %d size: %d", page->info.sequence, pageSize);
page->info.numLines = (pageSize - sizeof (M3CodePageHeader)) / sizeof (code_t);;
}
return page;
}
void FreeCodePages (IM3CodePage i_page)
{
while (i_page)
{
m3log (code, "free page: %d util: %3.1f%%", i_page->info.sequence, 100. * i_page->info.lineIndex / i_page->info.numLines);
IM3CodePage next = i_page->info.next;
free (i_page);
i_page = next;
}
}
//void CloseCodePage (IM3CodePage i_page)
//{
// i_page->info.slotIndex = c_m3CodePageNumSlots; // force closed
//}
u32 NumFreeLines (IM3CodePage i_page)
{
TestCodePageCapacity (i_page);
return i_page->info.numLines - i_page->info.lineIndex;
}
void EmitWord (IM3CodePage i_page, const void * const i_word)
{
i_page->code [i_page->info.lineIndex++] = (void *) i_word;
TestCodePageCapacity (i_page);
}
pc_t GetPagePC (IM3CodePage i_page)
{
if (i_page)
return & i_page->code [i_page->info.lineIndex];
else
return 0;
}
void PushCodePage (IM3CodePage * i_list, IM3CodePage i_codePage)
{
IM3CodePage next = * i_list;
i_codePage->info.next = next;
* i_list = i_codePage;
}
IM3CodePage PopCodePage (IM3CodePage * i_list)
{
IM3CodePage page = * i_list;
* i_list = page->info.next;
page->info.next = NULL;
return page;
}
void TestCodePageCapacity (IM3CodePage i_page)
{
#if DEBUG
assert (i_page->info.lineIndex <= i_page->info.numLines);
#endif
}

@ -0,0 +1,42 @@
//
// m3_code.h
// M3: Massey Meta Machine
//
// Created by Steven Massey on 4/19/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef m3_code_h
#define m3_code_h
#include "m3_core.h"
typedef struct M3CodePage
{
M3CodePageHeader info;
// code_t code [c_m3CodePageNumSlots];
code_t code [1];
}
M3CodePage;
typedef M3CodePage * IM3CodePage;
IM3CodePage NewCodePage (u32 i_minNumLines);
void FreeCodePages (IM3CodePage i_page);
//void CloseCodePage (IM3CodePage i_page);
u32 NumFreeLines (IM3CodePage i_page);
pc_t GetPagePC (IM3CodePage i_page);
void EmitWord (IM3CodePage i_page, const void * const i_word);
void PushCodePage (IM3CodePage * i_list, IM3CodePage i_codePage);
IM3CodePage PopCodePage (IM3CodePage * i_list);
void TestCodePageCapacity (IM3CodePage i_page);
void DumpCodePage (IM3CodePage i_codePage, pc_t i_startPC);
#endif /* m3_code_h */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,163 @@
//
// m3_compile.h
// M3: Massey Meta Machine
//
// Created by Steven Massey on 4/17/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef m3_compile_h
#define m3_compile_h
#include "m3_env.h"
#include "m3_exec_defs.h"
/*
WebAssembly spec info
---------------------
2.3.2 Result Types
Note: In the current version of WebAssembly, at most one value is allowed as a result. However, this may be
generalized to sequences of values in future versions.
M3
--
*/
enum
{
c_waOp_block = 0x02,
c_waOp_loop = 0x03,
c_waOp_if = 0x04,
c_waOp_else = 0x05,
c_waOp_end = 0x0b,
c_waOp_branch = 0x0c,
c_waOp_branchIf = 0x0d,
c_waOp_getLocal = 0x20,
c_waOp_setLocal = 0x21,
c_waOp_teeLocal = 0x22,
};
//-----------------------------------------------------------------------------------------------------------------------------------
// since the end location of a block can be unknown when a branch is compiled, writing
// the actual address must deferred. A linked-list of patch locations is kept in
// M3CompilationScope. When the block compilation exits, it patches these addresses.
typedef struct M3BranchPatch
{
struct M3BranchPatch * next;
pc_t * location;
}
M3BranchPatch;
typedef M3BranchPatch * IM3BranchPatch;
typedef struct M3CompilationScope
{
struct M3CompilationScope * outer;
pc_t pc; // used by ContinueLoop's
IM3BranchPatch patches;
i32 depth;
i32 loopDepth;
i16 initStackIndex;
u8 type;
u8 opcode;
}
M3CompilationScope;
typedef M3CompilationScope * IM3CompilationScope;
static const u16 c_m3RegisterUnallocated = 0;
typedef struct
{
IM3Runtime runtime;
IM3Module module;
const u8 * wasm;
cbytes_t wasmEnd;
M3CompilationScope block;
IM3Function function;
IM3CodePage page;
u32 numEmits;
u32 numOpcodes;
u16 firstSlotIndex; // numArgs + numLocals + numReservedConstants. the first mutable slot available to the compiler.
u16 stackIndex; // current stack index
u16 firstConstSlotIndex;
u16 constSlotIndex; // as const's are encountered during compilation this tracks their location in the "real" stack
u64 constants [c_m3MaxNumFunctionConstants];
// for args/locals this wasmStack tracks write counts. for the dynamic portion of the stack, the array holds slot locations
u16 wasmStack [c_m3MaxFunctionStackHeight];
u8 typeStack [c_m3MaxFunctionStackHeight];
// this array just contains single bit allocation flags. could be fused with the typeStack to conserve space
u8 m3Slots [c_m3MaxFunctionStackHeight];
u16 numAllocatedExecSlots;
u16 regStackIndexPlusOne [2];
bool enableOptimizations; // no longer used. currently implementation is highly pre-optimized.
u8 previousOpcode;
}
M3Compilation;
typedef M3Compilation * IM3Compilation;
typedef M3Result (* M3Compiler) (IM3Compilation, u8);
//-----------------------------------------------------------------------------------------------------------------------------------
typedef struct M3OpInfo
{
const char * const name;
i8 stackOffset;
u8 type;
IM3Operation operation_sr; // top operand in register
IM3Operation operation_rs; // top operand in stack
IM3Operation operation_ss; // both operands in stack
M3Compiler compiler;
}
M3OpInfo;
typedef const M3OpInfo * IM3OpInfo;
extern const M3OpInfo c_operations [];
//-----------------------------------------------------------------------------------------------------------------------------------
M3Result EmitOp (IM3Compilation o, IM3Operation i_operation);
void EmitConstant (IM3Compilation o, const u64 immediate);
void Push (IM3Compilation o, u8 i_waType, i16 i_location);
void EmitPointer (IM3Compilation o, const void * const i_immediate);
M3Result Compile_Block (IM3Compilation io, u8 i_blockType, u8 i_blockOpcode);
M3Result Compile_ElseBlock (IM3Compilation io, pc_t * o_startPC, u8 i_blockType);
M3Result Compile_BlockStatements (IM3Compilation io);
M3Result Compile_Function (IM3Function io_function);
bool PeekNextOpcode (IM3Compilation o, u8 i_opcode);
//M3Result Optimize_ConstOp (IM3Compilation o, u64 i_word, u8 i_waType);
#endif /* m3_compile_h */

@ -0,0 +1,61 @@
//
// m3_config.h
// m3
//
// Created by Steven Massey on 5/4/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef m3_config_h
#define m3_config_h
# ifndef d_m3CodePageSize
# define d_m3MaxNumFunctionArgs 32
# endif
# ifndef d_m3CodePageSize
# define d_m3CodePageSize 4096
# endif
# ifndef d_m3AlignWasmMemoryToPages
# define d_m3AlignWasmMemoryToPages false
# endif
# ifndef d_m3MaxFunctionStackHeight
# define d_m3MaxFunctionStackHeight 2000
# endif
# ifndef d_m3EnableOptimizations
# define d_m3EnableOptimizations false
# endif
# ifndef d_m3EnableFp32Maths
# define d_m3EnableFp32Maths false
# endif
# ifndef d_m3EnableFp64Maths
# define d_m3EnableFp64Maths true
# endif
# ifndef d_m3LogOutput
# define d_m3LogOutput true
# endif
// logging ---------------------------------------------------------------------------
# define d_m3EnableOpProfiling 0
# define d_m3RuntimeStackDumps 0
# define d_m3TraceExec (1 && d_m3RuntimeStackDumps && DEBUG)
// m3log (...) --------------------------------------------------------------------
# define d_m3LogParse 0
# define d_m3LogCompile 0
# define d_m3LogStack 0
# define d_m3LogEmit 0
# define d_m3LogCodePages 0
# define d_m3LogModule 0
# define d_m3LogRuntime 0
# define d_m3LogExec 0
#endif /* m3_config_h */

@ -0,0 +1,309 @@
//
//
// Created by Steven Massey on 4/15/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
// instantiate error strings here
# define d_m3ErrorConst(LABEL, STRING) const M3Result c_m3Err_##LABEL = { STRING };
#include "m3_core.h"
M3Result m3Malloc (void ** o_ptr, size_t i_size)
{
M3Result result = c_m3Err_none;
void * ptr = malloc (i_size);
if (ptr)
{
memset (ptr, 0x0, i_size);
}
else result = c_m3Err_mallocFailed;
* o_ptr = ptr;
return result;
}
void * m3Realloc (void * i_ptr, size_t i_newSize, size_t i_oldSize)
{
void * ptr = i_ptr;
if (i_newSize != i_oldSize)
{
ptr = realloc (i_ptr, i_newSize);
if (ptr)
{
if (i_ptr)
{
if (i_newSize > i_oldSize)
memset (ptr + i_oldSize, 0x0, i_newSize - i_oldSize);
}
else memset (ptr, 0x0, i_newSize);
}
}
return ptr;
}
//--------------------------------------------------------------------------------------------
bool IsFpType (u8 i_m3Type)
{
return (i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_f64);
}
bool IsIntType (u8 i_m3Type)
{
return (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_i64);
}
u32 SizeOfType (u8 i_m3Type)
{
u32 size = sizeof (i64);
if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32)
size = sizeof (i32);
return size;
}
M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType)
{
M3Result result = c_m3Err_none;
u8 type = -i_convolutedWasmType;
if (type == 0x40)
type = c_m3Type_none;
else if (type < c_m3Type_i32 or type > c_m3Type_f64)
result = c_m3Err_invalidTypeId;
* o_type = type;
return result;
}
//-- Binary Wasm parsing utils ------------------------------------------------------------------------------------------
M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end)
{
const u8 * ptr = * io_bytes;
ptr += sizeof (u64);
if (ptr <= i_end)
{
* o_value = * ((u64 *) * io_bytes);
* io_bytes = ptr;
return c_m3Err_none;
}
else return c_m3Err_wasmUnderrun;
}
M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end)
{
const u8 * ptr = * io_bytes;
ptr += sizeof (u32);
if (ptr <= i_end)
{
* o_value = * ((u32 *) * io_bytes);
* io_bytes = ptr;
return c_m3Err_none;
}
else return c_m3Err_wasmUnderrun;
}
M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end)
{
const u8 * ptr = * io_bytes;
if (ptr < i_end)
{
* o_value = * ptr;
ptr += sizeof (u8);
* io_bytes = ptr;
return c_m3Err_none;
}
else return c_m3Err_wasmUnderrun;
}
M3Result ReadLebUnsigned (u64 * o_value, i32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_wasmUnderrun;
u64 value = 0;
u32 shift = 0;
const u8 * ptr = * io_bytes;
while (ptr < i_end)
{
u64 byte = * (ptr++);
value |= ((byte & 0x7f) << shift);
shift += 7;
if ((byte & 0x80) == 0)
{
result = c_m3Err_none;
break;
}
if (shift > i_maxNumBits)
{
result = c_m3Err_lebOverflow;
break;
}
}
* o_value = value;
* io_bytes = ptr;
return result;
}
M3Result ReadLebSigned (i64 * o_value, i32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_wasmUnderrun;
i64 value = 0;
u32 shift = 0;
const u8 * ptr = * io_bytes;
while (ptr < i_end)
{
u64 byte = * (ptr++);
value |= ((byte & 0x7f) << shift);
shift += 7;
if ((byte & 0x80) == 0)
{
result = c_m3Err_none;
if (byte & 0x40) // do sign extension
{
u64 extend = 1;
extend <<= shift;
value |= -extend;
}
break;
}
if (shift > i_maxNumBits)
{
result = c_m3Err_lebOverflow;
break;
}
}
* o_value = value;
* io_bytes = ptr;
return result;
}
M3Result ReadLEB_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end)
{
u64 value;
M3Result result = ReadLebUnsigned (& value, 32, io_bytes, i_end);
* o_value = (u32) value;
return result;
}
M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end)
{
u64 value;
M3Result result = ReadLebUnsigned (& value, 7, io_bytes, i_end);
* o_value = (u8) value;
return result;
}
M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end)
{
i64 value;
M3Result result = ReadLebSigned (& value, 7, io_bytes, i_end);
* o_value = (i8) value;
return result;
}
M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end)
{
i64 value;
M3Result result = ReadLebSigned (& value, 32, io_bytes, i_end);
* o_value = (i32) value;
return result;
}
M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end)
{
i64 value;
M3Result result = ReadLebSigned (& value, 64, io_bytes, i_end);
* o_value = value;
return result;
}
M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end)
{
*o_utf8 = NULL;
u32 utf8Length;
M3Result result = ReadLEB_u32 (& utf8Length, io_bytes, i_end);
if (not result)
{
if (utf8Length and utf8Length <= c_m3MaxSaneUtf8Length)
{
const u8 * ptr = * io_bytes;
const u8 * end = ptr + utf8Length;
if (end <= i_end)
{
char * utf8;
result = m3Malloc ((void **) & utf8, utf8Length + 1);
if (not result)
{
memcpy (utf8, ptr, utf8Length);
utf8 [utf8Length] = 0;
* o_utf8 = utf8;
}
* io_bytes = end;
}
else result = c_m3Err_wasmUnderrun;
}
else result = c_m3Err_missingUTF8;
}
return result;
}

@ -0,0 +1,238 @@
//
// Created by Steven Massey on 4/15/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef M3UTIL_H
#define M3UTIL_H
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "m3.h"
#include "m3_config.h"
typedef double f64;
typedef float f32;
typedef uint64_t u64;
typedef int64_t i64;
typedef uint32_t u32;
typedef int32_t i32;
typedef uint16_t u16;
typedef int16_t i16;
typedef uint8_t u8;
typedef int8_t i8;
#if __LP64__
typedef i64 m3reg_t;
typedef u64 m3word_t;
typedef u32 m3halfword_t;
typedef i32 m3halfWordSigned_t;
// typedef i64 m3ret_t;
#else
typedef i32 m3reg_t;
typedef u32 m3word_t;
typedef u16 m3halfword_t;
// typedef i32 m3ret_t;
#endif
typedef const void * m3ret_t;
typedef const char * cstr_t;
typedef const char * const ccstr_t;
typedef const u8 * bytes_t;
typedef const u8 * const cbytes_t;
typedef u64 * m3stack_t;
typedef
const void * const cvptr_t;
# ifndef __cplusplus
# define not !
# define and &&
# define or ||
# endif
static const char * m3LogTruncFilename (const char * i_file)
{
const char * file = i_file + strlen (i_file);
while (true)
{
char c = * (file - 1);
if (c == '/' or c == '\\')
break;
--file;
}
return file;
}
# define d_m3Log_parse d_m3LogParse // required for m3logif
# define d_m3Log_stack d_m3LogStack
# define d_m3Log_exec d_m3LogExec
# if d_m3LogOutput
// with filename:
//# define d_m3Log(CATEGORY, FMT, ...) printf (" %-12s | %-8s | " FMT, m3LogTruncFilename (__FILE__), #CATEGORY, ##__VA_ARGS__);
# define d_m3Log(CATEGORY, FMT, ...) printf (" %8s | " FMT, #CATEGORY, ##__VA_ARGS__);
# if d_m3LogParse
# define m3log_parse(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__)
# else
# define m3log_parse(...)
# endif
# if d_m3LogCompile
# define m3log_compile(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__)
# else
# define m3log_compile(...)
# endif
# if d_m3LogStack
# define m3log_stack(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__)
# else
# define m3log_stack(...)
# endif
# if d_m3LogEmit
# define m3log_emit(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__)
# else
# define m3log_emit(...)
# endif
# if d_m3LogCodePages
# define m3log_code(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__)
# else
# define m3log_code(...)
# endif
# if d_m3LogModule
# define m3log_module(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__)
# else
# define m3log_module(...)
# endif
# if d_m3LogRuntime
# define m3log_runtime(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__)
# else
# define m3log_runtime(...)
# endif
# if d_m3LogExec
# define m3log_exec(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__)
# else
# define m3log_exec(...)
# endif
# define m3log(CATEGORY, FMT, ...) m3log_##CATEGORY (CATEGORY, FMT "\n", ##__VA_ARGS__)
# define m3logif(CATEGORY, STATEMENT) m3log_##CATEGORY (CATEGORY, ""); if (d_m3Log_##CATEGORY) { STATEMENT; printf ("\n"); }
# else
# define m3log(CATEGORY, FMT, ...)
# define m3logif(STATEMENT)
# endif
# if DEBUG
# define d_m3Assert(ASS) assert (ASS)
# else
# define d_m3Assert(ASS)
# endif
typedef void /*const*/ * code_t;
typedef code_t const * /*__restrict__*/ pc_t;
typedef struct M3CodePageInfo
{
struct M3CodePage * next;
u32 lineIndex;
u32 numLines;
u32 sequence; // this is just used for debugging; could be removed
u32 __;
}
M3CodePageHeader;
static const u32 c_m3CodePageFreeLinesThreshold = 10;
static const u32 c_m3MemPageSize = 65536;
static const u32 c_m3MaxFunctionStackHeight = d_m3MaxFunctionStackHeight;
static const u32 c_m3MaxFunctionLocals = 512;
static const u32 c_m3Reg0Id = c_m3MaxFunctionStackHeight + 1;
static const u32 c_m3Fp0Id = c_m3MaxFunctionStackHeight + 2;
static const u32 c_m3MaxNumFunctionConstants = 60;
static const bool c_m3AlignWasmMemoryToPages = d_m3AlignWasmMemoryToPages;
//static const u32 c_m3MaxSaneWasmSize = 1000000000;
static const u32 c_m3MaxSaneUtf8Length = 2000;
static const u32 c_m3MaxNumFunctionArgs = d_m3MaxNumFunctionArgs;
static const u8 c_externalKind_function = 0,
c_externalKind_table = 1,
c_externalKind_memory = 2,
c_externalKind_global = 3
;
static const char * const c_waTypes [] = { "nil", "i32", "i64", "f32", "f64", "void", "void *" };
#define m3Alloc(OPTR, STRUCT, NUM) m3Malloc ((void **) OPTR, sizeof (STRUCT) * (NUM))
#define m3RellocArray(PTR, STRUCT, NEW, OLD) m3Realloc ((PTR), sizeof (STRUCT) * (NEW), sizeof (STRUCT) * (OLD))
#define _m3Error(RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ...) m3Error (RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ##__VA_ARGS__)
#define ErrorModule(RESULT, MOD, FORMAT, ...) _m3Error (RESULT, MOD->runtime, MOD, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__)
#define ErrorCompile(RESULT, COMP, FORMAT, ...) _m3Error (RESULT, COMP->runtime, COMP->module, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__)
#define min(A,B) (A < B) ? A : B
#define max(A,B) (A > B) ? A : B
M3Result m3Malloc (void ** o_ptr, size_t i_size);
void * m3Realloc (void * i_ptr, size_t i_newSize, size_t i_oldSize);
bool IsIntType (u8 i_wasmType);
bool IsFpType (u8 i_wasmType);
M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType);
u32 SizeOfType (u8 i_m3Type);
M3Result Read_u64 (u64 * o_value, const u8 ** io_bytes, cbytes_t i_end);
M3Result Read_u32 (u32 * o_value, const u8 ** io_bytes, cbytes_t i_end);
M3Result Read_u8 (u8 * o_value, const u8 ** io_bytes, cbytes_t i_end);
M3Result ReadLebUnsigned (u64 * o_value, i32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end);
M3Result ReadLebSigned (i64 * o_value, i32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end);
M3Result ReadLEB_u32 (u32 * o_value, bytes_t* io_bytes, cbytes_t i_end);
M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end);
M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end);
M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end);
M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end);
M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end);
size_t SPrintArg (char * o_string, size_t i_n, m3stack_t i_sp, u8 i_type);
void ReportError (IM3Runtime io_runtime, IM3Module i_module, IM3Function i_function, ccstr_t i_errorMessage, ccstr_t i_file, u32 i_lineNum);
//static __inline__ unsigned long GetCC(void)
//{
// unsigned a, d;
// asm volatile("rdtsc" : "=a" (a), "=d" (d));
// return ((unsigned long)a) | (((unsigned long)d) << 32);
//}
#endif

@ -0,0 +1,103 @@
//
// m3_emit.c
// m3
//
// Created by Steven Massey on 7/9/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include "m3_emit.h"
M3Result EnsureCodePageNumLines (IM3Compilation o, u32 i_numLines)
{
M3Result result = c_m3Err_none;
i_numLines += 2; // room for Bridge
if (NumFreeLines (o->page) < i_numLines)
{
IM3CodePage page = AcquireCodePageWithCapacity (o->runtime, i_numLines);
if (page)
{
m3log (code, "bridging new code page from: %d (free slots: %d)", o->page->info.sequence, NumFreeLines (o->page));
EmitWord (o->page, op_Bridge);
EmitWord (o->page, GetPagePC (page));
ReleaseCodePage (o->runtime, o->page);
o->page = page;
}
else result = c_m3Err_mallocFailedCodePage;
}
return result;
}
// have execution jump to a new page if slots are critically low
M3Result BridgeToNewPageIfNecessary (IM3Compilation o)
{
return EnsureCodePageNumLines (o, c_m3CodePageFreeLinesThreshold - 2);
}
M3Result EmitOp (IM3Compilation o, IM3Operation i_operation)
{
M3Result result = c_m3Err_none;
// it's OK for page to be null; when compile-walking the bytecode without emitting
if (o->page)
{
if (i_operation != op_DumpStack)
o->numEmits++;
result = BridgeToNewPageIfNecessary (o);
if (not result)
EmitWord (o->page, i_operation);
}
return result;
}
// this pushes an immediate constant into the M3 codestream
void EmitConstant (IM3Compilation o, const u64 i_immediate)
{
if (o->page)
EmitWord (o->page, (const void *) i_immediate);
}
void EmitOffset (IM3Compilation o, const i32 i_offset)
{
if (o->page)
EmitWord (o->page, (const void *) (i64) i_offset);
}
void EmitPointer (IM3Compilation o, const void * const i_pointer)
{
if (o->page)
EmitWord (o->page, i_pointer);
}
void * ReservePointer (IM3Compilation o)
{
pc_t ptr = GetPagePC (o->page);
EmitPointer (o, NULL);
return (void *) ptr;
}
pc_t GetPC (IM3Compilation o)
{
return GetPagePC (o->page);
}

@ -0,0 +1,25 @@
//
// m3_emit.h
// m3
//
// Created by Steven Massey on 7/9/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef m3_emit_h
#define m3_emit_h
#include "m3_compile.h"
M3Result BridgeToNewPageIfNecessary (IM3Compilation o);
M3Result EnsureCodePageNumLines (IM3Compilation o, u32 i_numLines);
M3Result EmitOp (IM3Compilation o, IM3Operation i_operation);
void EmitConstant (IM3Compilation o, const u64 i_immediate);
void EmitOffset (IM3Compilation o, const i32 i_offset);
void EmitPointer (IM3Compilation o, const void * const i_pointer);
void * ReservePointer (IM3Compilation o);
pc_t GetPC (IM3Compilation o);
#endif /* m3_emit_h */

@ -0,0 +1,498 @@
//
// m3_env.c
// M3: Massey Meta Machine
//
// Created by Steven Massey on 4/19/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include <assert.h>
#include <stdarg.h>
#include "m3.h"
#include "m3_module.h"
#include "m3_compile.h"
#include "m3_exec.h"
#include "m3_exception.h"
ccstr_t GetFunctionName (IM3Function i_function)
{
return (i_function->name) ? i_function->name : ".unnamed";
}
u32 GetFunctionNumArgs (IM3Function i_function)
{
u32 numArgs = 0;
if (i_function->funcType)
numArgs = i_function->funcType->numArgs;
return numArgs;
}
u32 GetFunctionNumReturns (IM3Function i_function)
{
u32 numReturns = 0;
if (i_function->funcType)
numReturns = i_function->funcType->returnType ? 1 : 0;
return numReturns;
}
u8 GetFunctionReturnType (IM3Function i_function)
{
u8 returnType = c_m3Type_none;
if (i_function->funcType)
returnType = i_function->funcType->returnType;
return returnType;
}
u32 GetFunctionNumArgsAndLocals (IM3Function i_function)
{
if (i_function)
return i_function->numLocals + GetFunctionNumArgs (i_function);
else
return 0;
}
void FreeImportInfo (M3ImportInfo * i_info)
{
free ((void *) i_info->moduleUtf8); i_info->moduleUtf8 = NULL;
free ((void *) i_info->fieldUtf8); i_info->fieldUtf8 = NULL;
}
void InitRuntime (IM3Runtime io_runtime, u32 i_stackSizeInBytes)
{
m3Malloc (& io_runtime->stack, i_stackSizeInBytes);
io_runtime->numStackSlots = i_stackSizeInBytes / sizeof (m3word_t);
}
IM3Runtime m3_NewRuntime (u32 i_stackSizeInBytes)
{
m3_PrintM3Info ();
IM3Runtime env;
m3Alloc (& env, M3Runtime, 1);
if (env)
InitRuntime (env, i_stackSizeInBytes);
return env;
}
typedef void * (* ModuleVisitor) (IM3Module i_module, void * i_info);
void * ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info)
{
void * r = NULL;
IM3Module module = i_runtime->modules;
while (module)
{
IM3Module next = module->next;
r = i_visitor (module, i_info);
if (r)
break;
module = next;
}
return r;
}
void * _FreeModule (IM3Module i_module, void * i_info)
{
m3_FreeModule (i_module);
return NULL;
}
void ReleaseRuntime (IM3Runtime i_runtime)
{
ForEachModule (i_runtime, _FreeModule, NULL);
FreeCodePages (i_runtime->pagesOpen);
FreeCodePages (i_runtime->pagesFull);
free (i_runtime->stack);
}
void m3_FreeRuntime (IM3Runtime i_runtime)
{
ReleaseRuntime (i_runtime);
free (i_runtime);
}
M3Result EvaluateExpression (IM3Module i_module, void * o_expressed, u8 i_type, bytes_t * io_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_none;
// * o_expressed = 0;
u64 stack [c_m3MaxFunctionStackHeight]; // stack on the stack
// create a temporary runtime context
M3Runtime rt = {};
rt.numStackSlots = c_m3MaxFunctionStackHeight;
rt.stack = & stack;
IM3Runtime savedRuntime = i_module->runtime;
i_module->runtime = & rt;
M3Compilation o = { & rt, i_module, * io_bytes, i_end };
o.block.depth = -1; // so that root complation depth = 0
IM3CodePage page = o.page = AcquireCodePage (& rt);
if (page)
{
pc_t m3code = GetPagePC (page);
result = Compile_Block (& o, i_type, 0);
if (not result)
{
m3ret_t r = Call (m3code, stack, NULL, d_m3OpDefaultArgs);
result = rt.runtimeError;
if (r == 0 and not result)
{
if (SizeOfType (i_type) == sizeof (u32))
{
* (u32 *) o_expressed = * (u32 *) stack;
}
else if (SizeOfType (i_type) == sizeof (u64))
{
* (u64 *) o_expressed = * (u64 *) stack;
}
}
}
ReleaseCodePage (& rt, page);
}
else result = c_m3Err_mallocFailedCodePage;
rt.stack = NULL; // prevent free(stack) in ReleaseRuntime ()
ReleaseRuntime (& rt);
i_module->runtime = savedRuntime;
* io_bytes = o.wasm;
return result;
}
M3Result EvaluateExpression_i32 (IM3Module i_module, i32 * o_expressed, bytes_t * i_bytes, cbytes_t i_end)
{
return EvaluateExpression (i_module, o_expressed, c_m3Type_i32, i_bytes, i_end);
}
M3Result InitGlobals (IM3Module io_module)
{
M3Result result = c_m3Err_none;
if (io_module->numGlobals)
{
// placing the globals in their structs isn't good for cache locality, but i don't really know what the global
// access patterns typcially look like yet.
// io_module->globalMemory = m3Alloc (m3reg_t, io_module->numGlobals);
// if (io_module->globalMemory)
{
for (u32 i = 0; i < io_module->numGlobals; ++i)
{
M3Global * g = & io_module->globals [i]; m3log (runtime, "initializing global: %d", i);
// global fp types are coerced to double
if (g->type == c_m3Type_f32)
g->type = c_m3Type_f64;
if (g->initExpr)
{
bytes_t start = g->initExpr;
result = EvaluateExpression (io_module, & g->intValue, g->type, & start, g->initExpr + g->initExprSize);
if (not result)
{
// io_module->globalMemory [i] = initValue;
}
else break;
}
else
{ m3log (runtime, "importing global");
}
}
}
// else result = ErrorModule (c_m3Err_mallocFailed, io_module, "could allocate globals for module: '%s", io_module->name);
}
return result;
}
M3Result InitDataSegments (IM3Module io_module)
{
M3Result result = c_m3Err_none;
for (u32 i = 0; i < io_module->numDataSegments; ++i)
{
M3DataSegment * segment = & io_module->dataSegments [i];
i32 segmentOffset;
bytes_t start = segment->initExpr;
_ (EvaluateExpression_i32 (io_module, & segmentOffset, & start, segment->initExpr + segment->initExprSize));
u32 minMemorySize = segment->size + segmentOffset + 1; m3log (runtime, "loading data segment: %d offset: %d", i, segmentOffset);
_ (Module_EnsureMemorySize (io_module, & io_module->memory, minMemorySize));
memcpy (io_module->memory.wasmPages + segmentOffset, segment->data, segment->size);
}
catch: return result;
}
M3Result InitElements (IM3Module io_module)
{
M3Result result = c_m3Err_none;
bytes_t bytes = io_module->elementSection;
cbytes_t end = io_module->elementSectionEnd;
for (u32 i = 0; i < io_module->numElementSegments; ++i)
{
u32 index;
_ (ReadLEB_u32 (& index, & bytes, end));
if (index == 0)
{
i32 offset;
_ (EvaluateExpression_i32 (io_module, & offset, & bytes, end));
u32 numElements;
_ (ReadLEB_u32 (& numElements, & bytes, end));
u32 endElement = numElements + offset;
if (endElement > offset)
{
io_module->table0 = m3RellocArray (io_module->table0, IM3Function, endElement, io_module->table0Size);
if (io_module->table0)
{
io_module->table0Size = endElement;
for (u32 e = 0; e < numElements; ++e)
{
u32 functionIndex;
_ (ReadLEB_u32 (& functionIndex, & bytes, end));
if (functionIndex < io_module->numFunctions)
{
IM3Function function = & io_module->functions [functionIndex]; d_m3Assert (function); //printf ("table: %s\n", function->name);
io_module->table0 [e + offset] = function;
}
else throw ("function index out of range");
}
}
else throw (c_m3Err_mallocFailed);
}
else throw ("table overflow");
}
else throw ("element table index must be zero for MVP");
}
catch: return result;
}
M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module)
{
M3Result result = c_m3Err_none;
if (not io_module->runtime)
{
// assert (io_module->memory.actualSize == 0);
_ (InitGlobals (io_module));
_ (InitDataSegments (io_module));
_ (InitElements (io_module));
io_module->runtime = io_runtime;
io_module->next = io_runtime->modules;
io_runtime->modules = io_module;
}
else throw (c_m3Err_moduleAlreadyLinked);
catch: return result;
}
void * v_FindFunction (IM3Module i_module, const char * const i_name)
{
for (u32 i = 0; i < i_module->numFunctions; ++i)
{
IM3Function f = & i_module->functions [i];
if (f->name)
{
if (strcmp (f->name, i_name) == 0)
return f;
}
}
return NULL;
}
M3Result m3_FindFunction (IM3Function * o_function, IM3Runtime i_runtime, const char * const i_functionName)
{
M3Result result = c_m3Err_none;
IM3Function function = ForEachModule (i_runtime, (ModuleVisitor) v_FindFunction, (void *) i_functionName);
if (function)
{
if (not function->compiled)
{
result = Compile_Function (function);
if (result)
function = NULL;
}
}
else result = c_m3Err_functionLookupFailed;
* o_function = function;
return result;
}
M3Result m3_Call (IM3Function i_function)
{
M3Result result = c_m3Err_none;
if (i_function->compiled)
{
IM3Runtime env = i_function->module->runtime;
m3stack_t stack = env->stack;
stack += i_function->numLocals;
_ (Module_EnsureMemorySize (i_function->module, & i_function->module->memory, 3000000));
u8 * linearMemory = i_function->module->memory.wasmPages;
_ (Call (i_function->compiled, stack, linearMemory, d_m3OpDefaultArgs));
u64 value = * (u64 *) (stack);
m3log (runtime, "return64: %llu return32: %u", value, (u32) value);
}
else throw (c_m3Err_missingCompiledCode);
catch: return result;
}
IM3CodePage AcquireCodePage (IM3Runtime i_runtime)
{
if (i_runtime->pagesOpen)
return PopCodePage (& i_runtime->pagesOpen);
else
return NewCodePage (500); // for 4kB page
}
IM3CodePage AcquireCodePageWithCapacity (IM3Runtime i_runtime, u32 i_lineCount)
{
IM3CodePage page;
if (i_runtime->pagesOpen)
{
page = PopCodePage (& i_runtime->pagesOpen);
if (NumFreeLines (page) < i_lineCount)
{
IM3CodePage tryAnotherPage = AcquireCodePageWithCapacity (i_runtime, i_lineCount);
ReleaseCodePage (i_runtime, page);
page = tryAnotherPage;
}
}
else page = NewCodePage (i_lineCount);
return page;
}
void ReleaseCodePage (IM3Runtime i_runtime, IM3CodePage i_codePage)
{
// DumpCodePage (i_codePage, /* startPC: */ NULL);
if (i_codePage)
{
IM3CodePage * list;
if (NumFreeLines (i_codePage) < c_m3CodePageFreeLinesThreshold)
list = & i_runtime->pagesFull;
else
list = & i_runtime->pagesOpen;
PushCodePage (list, i_codePage);
}
}
//void CloseCodePage (IM3Runtime i_runtime, IM3CodePage i_codePage)
//{
// i_codePage->info.lineIndex = c_m3CodePageFreeLinesThreshold;
// ReleaseCodePage (i_runtime, i_codePage);
//}
M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function,
const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...)
{
if (i_runtime)
{
M3ErrorInfo info = { i_result, i_runtime, i_module, i_function, i_file, i_lineNum };
va_list args;
va_start (args, i_errorMessage);
vsnprintf (info.message, 1023, i_errorMessage, args);
va_end (args);
i_runtime->error = info;
}
return i_result;
}
M3ErrorInfo m3_GetErrorInfo (IM3Runtime i_runtime)
{
M3ErrorInfo info = i_runtime->error;
M3ErrorInfo reset = {};
i_runtime->error = reset;
return info;
}

@ -0,0 +1,226 @@
//
// m3_env.h
// M3: Massey Meta Machine
//
// Created by Steven Massey on 4/19/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef m3_env_h
#define m3_env_h
#include "m3.h"
#include "m3_code.h"
#include "m3_exec.h"
typedef struct M3FuncType
{
u32 numArgs;
u8 argTypes [d_m3MaxNumFunctionArgs];
u8 returnType;
}
M3FuncType;
typedef M3FuncType * IM3FuncType;
void PrintFuncTypeSignature (IM3FuncType i_funcType);
//---------------------------------------------------------------------------------------------------------------------------------
typedef struct M3Function
{
struct M3Module * module;
M3ImportInfo import;
bytes_t wasm;
bytes_t wasmEnd;
cstr_t name;
m3word_t hits;
IM3FuncType funcType;
IM3Operation callOp;
pc_t compiled;
u32 numLocals; // not including args
u32 numConstants;
void * constants;
bool callNeedsRuntime;
}
M3Function;
typedef M3Function * IM3Function;
ccstr_t GetFunctionName (IM3Function i_function);
u32 GetFunctionNumArgs (IM3Function i_function);
u32 GetFunctionNumReturns (IM3Function i_function);
u8 GetFunctionReturnType (IM3Function i_function);
u32 GetFunctionNumArgsAndLocals (IM3Function i_function);
ccstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp);
//---------------------------------------------------------------------------------------------------------------------------------
typedef struct M3MemoryHeader
{
IM3Module module;
void * end;
}
M3MemoryHeader;
typedef struct M3Memory
{
M3MemoryHeader * mallocated;
u8 * wasmPages; // = mallocated + sizeof (M3Memory)
size_t virtualSize;
size_t heapOffset;
size_t heapAllocated;
}
M3Memory;
//---------------------------------------------------------------------------------------------------------------------------------
typedef struct M3DataSegment
{
const u8 * initExpr; // wasm code
const u8 * data;
u32 initExprSize;
u32 memoryRegion;
u32 size;
}
M3DataSegment;
void FreeImportInfo (M3ImportInfo * i_info);
//---------------------------------------------------------------------------------------------------------------------------------
typedef struct M3Global
{
M3ImportInfo import;
union
{
i64 intValue;
f64 f64Value;
f32 f32Value;
};
bytes_t initExpr; // wasm code
u32 initExprSize;
u8 type;
bool imported;
}
M3Global;
typedef M3Global * IM3Global;
//---------------------------------------------------------------------------------------------------------------------------------
typedef struct M3Module // TODO add env owner? also discriminates stack/heap
{
struct M3Runtime * runtime;
cstr_t name;
u32 numFuncTypes;
M3FuncType * funcTypes;
u32 numImports;
IM3Function * imports; // notice: "I" prefix. imports are pointers to functions in another module.
u32 numFunctions;
M3Function * functions;
u32 numDataSegments;
M3DataSegment * dataSegments;
u32 importedGlobals;
u32 numGlobals;
M3Global * globals;
u32 numElementSegments;
bytes_t elementSection;
bytes_t elementSectionEnd;
IM3Function * table0;
u32 table0Size;
M3Memory memory;
// m3reg_t * globalMemory;
struct M3Module * next;
}
M3Module;
typedef M3Module * IM3Module;
M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_type, bool i_mutable, bool i_isImported);
M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo /* can be null */);
IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex);
//---------------------------------------------------------------------------------------------------------------------------------
typedef struct M3Runtime
{
M3CodePage * pagesOpen; // linked list of code pages with writable space on them
M3CodePage * pagesFull; // linked list of finalized pages
IM3Module modules; // linked list of imported modules
void * stack;
// void * stackPtr; // TODO: args
u32 numStackSlots;
M3Result runtimeError;
// u32 numFuncTypes;
// M3FuncType * funcTypes;
M3ErrorInfo error;
}
M3Runtime;
typedef M3Runtime * IM3Runtime;
void InitRuntime (IM3Runtime io_runtime, u32 i_stackSizeInBytes);
void ReleaseRuntime (IM3Runtime io_runtime);
typedef void * (* ModuleVisitor) (IM3Module i_module, void * i_info);
void * ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info);
void * v_FindFunction (IM3Module i_module, const char * const i_name);
IM3CodePage AcquireCodePage (IM3Runtime io_runtime);
IM3CodePage AcquireCodePageWithCapacity (IM3Runtime io_runtime, u32 i_slotCount);
void ReleaseCodePage (IM3Runtime io_runtime, IM3CodePage i_codePage);
//void CloseCodePage (IM3Runtime io_runtime, IM3CodePage i_codePage);
// Wasm MVP: i_memoryIndex must be zero
//void * m3_GetMemory (IM3Runtime i_runtime, u32 i_memoryIndex);
//size_t m3_GetMemorySize (void * i_memory);
M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function, const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...);
#endif /* m3_env_h */

@ -0,0 +1,17 @@
//
// m3_exception.h
// m3
//
// Created by Steven Massey on 7/5/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef m3_exception_h
#define m3_exception_h
// some macros to emulate try/catch
#define _(TRY) { result = TRY; if (result) goto catch; }
#define throw(ERROR) { result = ERROR; if (result) goto catch; }
#endif /* m3_exception_h */

@ -0,0 +1,291 @@
//
// m3_exec.c
// M3: Massey Meta Machine
//
// Created by Steven Massey on 4/17/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include "m3_exec.h"
#include "m3_compile.h"
void ReportError2 (IM3Function i_function, M3Result i_result)
{
i_function->module->runtime->runtimeError = i_result;
}
d_m3OpDef (Call)
{
pc_t callPC = immediate (pc_t);
i32 stackOffset = immediate (i32);
m3stack_t sp = _sp + stackOffset;
m3ret_t r = Call (callPC, sp, _mem, d_m3OpDefaultArgs);
if (r == 0)
r = nextOp ();
return r;
}
// TODO: type not checked.
d_m3OpDef (CallIndirect)
{
IM3Module module = immediate (IM3Module);
IM3FuncType type = immediate (IM3FuncType);
i32 stackOffset = immediate (i32);
m3stack_t sp = _sp + stackOffset;
i32 tableIndex = * (i32 *) (sp + type->numArgs);
if (tableIndex >= 0 and tableIndex < module->table0Size)
{
m3ret_t r = c_m3Err_none;
IM3Function function = module->table0 [tableIndex];
if (function)
{
if (not function->compiled)
r = Compile_Function (function);
if (not r)
{
r = Call (function->compiled, sp, _mem, d_m3OpDefaultArgs);
if (not r)
r = nextOp ();
}
}
else r = "trap: table element is null";
return r;
}
else return c_m3Err_trapTableIndexOutOfRange;
}
// it's a debate: should the compilation be trigger be the caller or callee page.
// it's a much easier to put it in the caller pager. if it's in the callee, either the entire page
// has be left dangling or it's just a stub that jumps to a newly acquire page. In Gestalt, I opted
// for the stub approach. Stubbing makes it easier to dynamically free the compilation. You can also
// do both.
d_m3OpDef (Compile)
{
rewrite (op_Call);
IM3Function function = immediate (IM3Function);
M3Result result = c_m3Err_none;
if (not function->compiled) // check to see if function was compiled since this operation was emitted.
result = Compile_Function (function);
if (not result)
{
// patch up compiled pc and call rewriten op_Call
*((m3word_t *) --_pc) = (m3word_t) (function->compiled);
--_pc;
result = nextOp ();
}
else ReportError2 (function, result);
return result;
}
d_m3OpDef (Entry)
{
IM3Function function = immediate (IM3Function);
function->hits++; m3log (exec, " enter > %s %s", function->name, SPrintFunctionArgList (function, _sp));
u32 numLocals = function->numLocals;
m3stack_t stack = _sp + GetFunctionNumArgs (function);
while (numLocals--) // it seems locals need to init to zero (at least for optimized Wasm code)
* (stack++) = 0;
memcpy (stack, function->constants, function->numConstants * sizeof (u64));
m3ret_t r = nextOp ();
if (d_m3Log_exec)
{
char str [100];
SPrintArg (str, 99, _sp, function->funcType->returnType);
m3log (exec, " exit < %s -> %s %s\n", function->name, str, r ? r : "");
}
return r;
}
d_m3OpDef (DumpStack)
{
u32 opcodeIndex = immediate (u32);
u64 stackHeight = immediate (u64);
IM3Function function = immediate (IM3Function);
cstr_t funcName = (function) ? function->name : "";
printf (" %4d ", opcodeIndex);
printf (" %-25s r0: 0x%016llx i:%lld u:%llu \n", funcName, _r0, _r0, _r0);
printf (" fp0: %lf \n", _fp0);
u64 * sp = _sp;
for (u32 i = 0; i < stackHeight; ++i)
{
printf ("%016llx ", (u64) sp);
cstr_t kind = "";
printf ("%5s %2d: 0x%llx %lld\n", kind, i, (u64) *(sp), (i64) *sp);
++sp;
}
printf ("---------------------------------------------------------------------------------------------------------\n");
return _ (_pc, d_m3OpArgs);
}
# if d_m3EnableOpProfiling
M3ProfilerSlot s_opProfilerCounts [c_m3ProfilerSlotMask] = {};
void ProfileHit (cstr_t i_operationName)
{
u64 ptr = (u64) i_operationName;
M3ProfilerSlot * slot = & s_opProfilerCounts [ptr & c_m3ProfilerSlotMask];
if (slot->opName)
{
if (slot->opName != i_operationName)
{
printf ("**** profiler slot collision; increase mask width\n");
abort ();
}
}
slot->opName = i_operationName;
slot->hitCount++;
}
# endif
void m3_PrintProfilerInfo ()
{
# if d_m3EnableOpProfiling
for (u32 i = 0; i <= c_m3ProfilerSlotMask; ++i)
{
M3ProfilerSlot * slot = & s_opProfilerCounts [i];
if (slot->opName)
printf ("%13llu %s\n", slot->hitCount, slot->opName);
}
# endif
}
d_m3OpDef (Loop)
{
m3ret_t r;
do
{
r = nextOp (); // printf ("loop: %p\n", r);
}
while (r == _pc);
return r;
}
d_m3OpDef (If_r)
{
i32 condition = (i32) _r0;
immediate (pc_t); // empty preservation chain
pc_t elsePC = immediate (pc_t);
if (condition)
return nextOp ();
else
return d_else (elsePC);
}
d_m3OpDef (If_s)
{
i32 condition = slot (i32);
immediate (pc_t); // empty preservation chain
pc_t elsePC = immediate (pc_t);
if (condition)
return nextOp ();
else
return d_else (elsePC);
}
d_m3OpDef (IfPreserve)
{
i32 condition = (i32) _r0;
d_call (immediate (pc_t));
pc_t elsePC = immediate (pc_t); //printf ("else: %p\n", elsePC);
if (condition)
return nextOp ();
else
return d_else (elsePC);
}
d_m3OpDef (Trap)
{ m3log (exec, "*** trapping ***");
return c_m3Err_runtimeTrap;
}
d_m3OpDef (End)
{
return 0;
}
d_m3OpDef (i32_Remainder)
{
abort (); // fix
if (_r0 != 0)
{
i32 op0 = * ((i32 *) --_sp);
// max negative divided by -1 overflows max positive
if (op0 == 0x80000000 and _r0 == -1)
_r0 = 0;
else
_r0 = op0 % _r0;
return nextOp ();
}
else return c_m3Err_trapRemainderByZero;
}

@ -0,0 +1,867 @@
//
// m3_exec.h
// M3: Massey Meta Machine
//
// Created by Steven Massey on 4/17/19.
// Copyright © 2019 Steven Massey. All rights reserved.
#ifndef m3_exec_h
#define m3_exec_h
#include "m3_exec_defs.h"
#include <math.h>
# define rewrite(NAME) * ((void **) (_pc-1)) = NAME
# define d_m3RetSig static inline m3ret_t vectorcall
# define d_m3Op(NAME) d_m3RetSig op_##NAME (d_m3OpSig)
# define d_m3OpDef(NAME) m3ret_t vectorcall op_##NAME (d_m3OpSig)
# define d_m3OpDecl(NAME) d_m3OpDef (NAME);
# define immediate(TYPE) * ((TYPE *) _pc++)
# define slot(TYPE) * (TYPE *) (_sp + immediate (i32))
# if d_m3EnableOpProfiling
# define nextOp() profileOp (_pc, d_m3OpArgs, __PRETTY_FUNCTION__)
# endif
# if d_m3TraceExec
# define nextOp() debugOp (d_m3OpAllArgs, __PRETTY_FUNCTION__)
# endif
# ifndef nextOp
# define nextOp() _ (_pc, d_m3OpArgs)
# endif
#define d_call(PC) Call (PC, d_m3OpArgs)
#define d_else(PC) Else (PC, d_m3OpArgs)
d_m3RetSig Call (d_m3OpSig)
{
IM3Operation operation = (* _pc);
return operation (_pc + 1, d_m3OpArgs);
}
d_m3RetSig Else (d_m3OpSig)
{
IM3Operation operation = (* _pc);
return operation (_pc + 1, d_m3OpArgs);
}
d_m3RetSig _ (d_m3OpSig)
{
IM3Operation operation = (* _pc);
return operation (_pc + 1, d_m3OpArgs);
}
d_m3RetSig debugOp (d_m3OpSig, cstr_t i_opcode)
{
char name [100];
strcpy (name, strstr (i_opcode, "op_") + 3);
* strstr (name, "(") = 0;
printf ("%s\n", name);
return _ (d_m3OpAllArgs);
}
static const u32 c_m3ProfilerSlotMask = 0xFFFF;
typedef struct M3ProfilerSlot
{
cstr_t opName;
u64 hitCount;
}
M3ProfilerSlot;
void ProfileHit (cstr_t i_operationName);
d_m3RetSig profileOp (d_m3OpSig, cstr_t i_operationName)
{
ProfileHit (i_operationName);
return _ (_pc, d_m3OpArgs);
}
d_m3OpDecl (DumpStack)
#define d_m3CompareOp(REG, TYPE, NAME, OPERATION) d_m3RetSig op_##TYPE##_##NAME##_sr (d_m3OpSig) \
{ \
TYPE * stack = (TYPE *) (_sp + immediate (i32)); \
_r0 = * stack OPERATION (TYPE) REG; \
return nextOp (); \
} \
\
d_m3RetSig op_##TYPE##_##NAME##_ss(d_m3OpSig) \
{ \
TYPE * stackB = (TYPE *) (_sp + immediate (i32)); \
TYPE * stackA = (TYPE *) (_sp + immediate (i32)); \
_r0 = * stackA OPERATION * stackB; \
return nextOp (); \
} \
\
d_m3RetSig op_##TYPE##_##NAME##_rs (d_m3OpSig) \
{ \
TYPE * stack = (TYPE *) (_sp + immediate (i32)); \
_r0 = (TYPE) REG OPERATION (* stack); \
return nextOp (); \
} \
// compare needs to be distinct for fp 'cause the result must be _r0
#define d_m3CompareOp_f(TYPE, NAME, OP) d_m3CompareOp (_fp0, TYPE, NAME, OP)
//-----------------------
#define d_m3CommutativeOp(REG, TYPE, NAME, OPERATION) d_m3RetSig op_##TYPE##_##NAME##_sr (d_m3OpSig) \
{ \
TYPE * stack = (TYPE *) (_sp + immediate (i32)); \
REG = * stack OPERATION (TYPE) REG; \
return nextOp (); \
} \
\
d_m3RetSig op_##TYPE##_##NAME##_ss(d_m3OpSig) \
{ \
TYPE * stackB = (TYPE *) (_sp + immediate (i32)); \
TYPE * stackA = (TYPE *) (_sp + immediate (i32)); \
REG = * stackA OPERATION * stackB; \
return nextOp (); \
}
#define d_m3Op_(REG, TYPE, NAME, OPERATION) d_m3RetSig op_##TYPE##_##NAME##_rs (d_m3OpSig) \
{ \
TYPE * stack = (TYPE *) (_sp + immediate (i32)); \
REG = (TYPE) REG OPERATION (* stack); \
return nextOp (); \
} \
d_m3CommutativeOp(REG, TYPE,NAME,OPERATION)
#define d_m3CommutativeOp_i(TYPE, NAME, OP) d_m3CommutativeOp (_r0, TYPE, NAME, OP)
#define d_m3Op_i(TYPE, NAME, OP) d_m3Op_ (_r0, TYPE, NAME, OP)
#define d_m3CommutativeOp_f(TYPE, NAME, OP) d_m3CommutativeOp (_fp0, TYPE, NAME, OP)
#define d_m3Op_f(TYPE, NAME, OP) d_m3Op_ (_fp0, TYPE, NAME, OP)
// signed
d_m3Op_i (i32, Equal, ==) d_m3Op_i (i64, Equal, ==)
d_m3Op_i (i32, NotEqual, !=) d_m3Op_i (i64, NotEqual, !=)
d_m3Op_i (i32, LessThan, <) d_m3Op_i (i64, LessThan, <)
d_m3Op_i (i32, GreaterThan, >) d_m3Op_i (i64, GreaterThan, >)
d_m3Op_i (i32, LessThanOrEqual, <=) d_m3Op_i (i64, LessThanOrEqual, <=)
d_m3Op_i (i32, GreaterThanOrEqual, >=) d_m3Op_i (i64, GreaterThanOrEqual, >=)
d_m3CompareOp_f (f32, Equal, ==)
d_m3CompareOp_f (f32, NotEqual, !=)
d_m3CompareOp_f (f32, LessThan, <)
d_m3CompareOp_f (f32, GreaterThan, >)
d_m3CompareOp_f (f32, LessThanOrEqual, <=)
d_m3CompareOp_f (f32, GreaterThanOrEqual, >=)
d_m3CompareOp_f (f64, Equal, ==)
d_m3CompareOp_f (f64, NotEqual, !=)
d_m3CompareOp_f (f64, LessThan, <)
d_m3CompareOp_f (f64, GreaterThan, >)
d_m3CompareOp_f (f64, LessThanOrEqual, <=)
d_m3CompareOp_f (f64, GreaterThanOrEqual, >=)
// unsigned
d_m3Op_i (u32, LessThan, <) d_m3Op_i (u64, LessThan, <)
d_m3Op_i (u32, GreaterThan, >) d_m3Op_i (u64, GreaterThan, >)
d_m3Op_i (u32, LessThanOrEqual, <=) d_m3Op_i (u64, LessThanOrEqual, <=)
d_m3Op_i (u32, GreaterThanOrEqual, >=) d_m3Op_i (u64, GreaterThanOrEqual, >=)
// are these supposed to trap? sounds like it
// "Signed and unsigned operators trap whenever the result cannot be represented in the result type."
d_m3CommutativeOp_i (i32, Add, +) d_m3CommutativeOp_i (i64, Add, +)
d_m3CommutativeOp_i (i32, Multiply, *) d_m3CommutativeOp_i (i64, Multiply, *)
d_m3Op_i (i32, Subtract, -) d_m3Op_i (i64, Subtract, -)
d_m3Op_i (i32, Remainder, %)
// TODO: trap division by zero
d_m3Op_i (i32, Divide, /) d_m3Op_i (i64, Divide, /)
d_m3Op_i (u32, Divide, /) d_m3Op_i (u64, Divide, /)
d_m3OpDecl (i32_Remainder)
d_m3Op_i (i32, ShiftLeft, <<) d_m3Op_i (i64, ShiftLeft, <<)
d_m3Op_i (i32, ShiftRight, >>) d_m3Op_i (i64, ShiftRight, >>)
d_m3Op_i (u32, ShiftRight, >>) d_m3Op_i (u64, ShiftRight, >>)
d_m3CommutativeOp_i (u64, And, &)
d_m3CommutativeOp_i (u64, Or, |)
d_m3CommutativeOp_i (u64, Xor, ^)
d_m3Op_f (f32, Add, +) d_m3Op_f (f64, Add, +)
d_m3Op_f (f32, Subtract, -) d_m3Op_f (f64, Subtract, -)
d_m3Op_f (f32, Multiply, *) d_m3Op_f (f64, Multiply, *)
#define d_m3CommutativeFuncOp(REG, TYPE, NAME, FUNC) d_m3RetSig op_##TYPE##_##NAME##_sr (d_m3OpSig) \
{ \
TYPE * stack = (TYPE *) (_sp + immediate (i32)); \
m3ret_t r = FUNC (& REG, * stack, (TYPE) REG); \
if (r) return r; \
else return nextOp (); \
} \
\
d_m3RetSig op_##TYPE##_##NAME##_ss(d_m3OpSig) \
{ \
TYPE * stackB = (TYPE *) (_sp + immediate (i32)); \
TYPE * stackA = (TYPE *) (_sp + immediate (i32)); \
m3ret_t r = FUNC (& REG, * stackA, * stackB); \
if (r) return r; \
else return nextOp (); \
}
#define d_m3FuncOp_(REG, TYPE, NAME, FUNC) d_m3RetSig op_##TYPE##_##NAME##_rs (d_m3OpSig) \
{ \
TYPE * stack = (TYPE *) (_sp + immediate (i32)); \
m3ret_t r = FUNC (& REG, * stack, (TYPE) REG); \
if (r) return r; \
else return nextOp (); \
} \
d_m3CommutativeFuncOp(REG, TYPE, NAME, FUNC)
#define d_m3FuncOp_i(TYPE, NAME, FUNC) d_m3FuncOp_ (_r0, TYPE, NAME, FUNC)
inline m3ret_t Remainder_u32 (i64 * o_result, u32 i_op1, u32 i_op2)
{
if (i_op2 != 0)
{
// // max negative divided by -1 overflows max positive
// if (op0 == 0x80000000 and _r0 == -1)
// _r0 = 0;
// else
// _r0 = op0 % _r0;
u32 result = i_op1 % i_op2;
* o_result = result;
return 0;
}
else return c_m3Err_trapRemainderByZero;
}
d_m3FuncOp_i (u32, Remainder, Remainder_u32);
/*
#define d_m3Op_Divide_f(TYPE, NAME, OPERATION) d_m3RetSig op_##TYPE##_##NAME (d_m3OpSig) \
{ \
if (_fp0 != 0.) \
{ \
f64 op0 = * ((f64 *) --_sp); \
_fp0 = (TYPE) op0 OPERATION (TYPE) _fp0; \
return nextOp (); \
} \
else return c_m3Trap_divisionByZero; \
}
d_m3Op_Divide_f (f32, Divide, /)
d_m3Op_Divide_f (f64, Divide, /)
*/
d_m3Op_f (f32, Divide, /)
d_m3Op_f (f64, Divide, /)
#define d_m3BinaryOp2(TYPE, NAME, OPERATION) d_m3RetSig op_##TYPE##_##NAME (d_m3OpSig) \
{ \
TYPE op1 = * ((TYPE *) --_sp); \
TYPE * op0 = (TYPE *) (_sp - 1); \
* op0 = OPERATION (*op0, op1); \
abort ();\
return nextOp (); \
}
static inline f32 min32 (f32 a, f32 b) { return a < b ? a : b; } d_m3BinaryOp2 (f32, Min, min32);
static inline f64 min64 (f64 a, f64 b) { return a < b ? a : b; } d_m3BinaryOp2 (f64, Min, min64);
static inline f32 max32 (f32 a, f32 b) { return a > b ? a : b; } d_m3BinaryOp2 (f32, Max, max32);
static inline f64 max64 (f64 a, f64 b) { return a > b ? a : b; } d_m3BinaryOp2 (f64, Max, max64);
d_m3BinaryOp2 (f32, CopySign, copysignf); d_m3BinaryOp2 (f64, CopySign, copysign);
#define d_m3UnaryOp2(TYPE, NAME, OPERATION) d_m3RetSig op_##TYPE##_##NAME (d_m3OpSig) \
{ \
abort (); \
TYPE * op = (TYPE *) (_sp - 1); \
* op = OPERATION (* op); \
return nextOp (); \
}
d_m3UnaryOp2 (f32, Abs, fabsf); d_m3UnaryOp2 (f64, Abs, fabs);
d_m3UnaryOp2 (f32, Ceil, ceilf); d_m3UnaryOp2 (f64, Ceil, ceil);
d_m3UnaryOp2 (f32, Floor, floorf); d_m3UnaryOp2 (f64, Floor, floor);
d_m3UnaryOp2 (f32, Trunc, truncf); d_m3UnaryOp2 (f64, Trunc, trunc);
d_m3UnaryOp2 (f32, Sqrt, sqrtf); d_m3UnaryOp2 (f64, Sqrt, sqrt);
// "unary"
#define d_m3UnaryOp_i(TYPE, NAME, OPERATION) d_m3RetSig op_##TYPE##_##NAME##_r (d_m3OpSig) \
{ \
_r0 = (TYPE) _r0 OPERATION; \
return nextOp (); \
} \
d_m3RetSig op_##TYPE##_##NAME##_s (d_m3OpSig) \
{ \
TYPE * stack = (TYPE *) (_sp + immediate (i32));\
_r0 = * stack OPERATION; \
return nextOp (); \
}
d_m3UnaryOp_i (i32, EqualToZero, == 0)
d_m3UnaryOp_i (i64, EqualToZero, == 0)
#define d_m3IntToFpConvertOp(TO, NAME, FROM) d_m3RetSig op_##TO##_##NAME##_##FROM##_r (d_m3OpSig) \
{ \
_fp0 = (TO) ((FROM) _r0); \
return nextOp (); \
} \
\
d_m3RetSig op_##TO##_##NAME##_##FROM##_s (d_m3OpSig) \
{ \
FROM * stack = (FROM *) (_sp + immediate (i32)); \
_fp0 = (TO) (* stack); \
return nextOp (); \
}
d_m3IntToFpConvertOp (f64, Convert, i32);
//d_m3IntToFpConversionOp (f64, Convert, u32);
//d_m3IntToFpConversionOp (f64, Convert, i64);
//d_m3IntToFpConversionOp (f64, Convert, u64);
#define d_m3FpToFpConvertOp(TO, NAME) d_m3RetSig op_##TO##_##NAME##_r (d_m3OpSig) \
{ \
_fp0 = (TO) _fp0; \
return nextOp (); \
} \
\
d_m3RetSig op_##TO##_##NAME##_s (d_m3OpSig) \
{ \
f64 * stack = (f64 *) (_sp + immediate (i32)); \
_fp0 = (TO) (* stack); \
return nextOp (); \
}
d_m3FpToFpConvertOp (f32, Demote)
d_m3Op (Extend_u)
{
_r0 = (u32) _r0;
return nextOp ();
}
d_m3Op (Extend_s)
{
_r0 = (i32) _r0;
return nextOp ();
}
d_m3Op (Nop)
{
return nextOp ();
}
#define d_m3TruncateOp(TO, NAME, FROM, TEST) d_m3RetSig op_##TO##_##NAME##_##FROM (d_m3OpSig) \
{ \
FROM from = _fp0; \
if (TEST (from)) \
{ \
_r0 = (TO) from; \
return nextOp (); \
} \
else return c_m3Err_trapTruncationOverflow; \
}
/*
FIX: should trap
Truncation from floating point to integer where IEEE 754-2008 would specify an invalid operator exception
(e.g. when the floating point value is NaN or outside the range which rounds to an integer in range) traps.
*/
static inline bool IsValid_i32 (f64 i_value)
{
if (not isnan (i_value))
{
if (i_value >= INT32_MIN and i_value <= INT32_MAX)
return true;
}
return false;
}
d_m3TruncateOp (i32, Truncate, f64, IsValid_i32);
d_m3Op (Block)
{
return nextOp ();
}
d_m3OpDecl (Loop)
d_m3OpDecl (If_r)
d_m3OpDecl (If_s)
d_m3Op (Select_i_ssr)
{
i32 condition = (i32) _r0;
i64 operand2 = * (_sp + immediate (i32));
i64 operand1 = * (_sp + immediate (i32));
_r0 = (condition) ? operand1 : operand2;
return nextOp ();
}
d_m3Op (Select_i_srs)
{
i32 condition = (i32) * (_sp + immediate (i32));
i64 operand2 = _r0;
i64 operand1 = * (_sp + immediate (i32));
_r0 = (condition) ? operand1 : operand2;
return nextOp ();
}
d_m3Op (Select_i_rss)
{
i32 condition = (i32) * (_sp + immediate (i32));
i64 operand2 = * (_sp + immediate (i32));
i64 operand1 = _r0;
_r0 = (condition) ? operand1 : operand2;
return nextOp ();
}
d_m3Op (Select_i_sss)
{
i32 condition = (i32) * (_sp + immediate (i32));
i64 operand2 = * (_sp + immediate (i32));
i64 operand1 = * (_sp + immediate (i32));
_r0 = (condition) ? operand1 : operand2;
return nextOp ();
}
d_m3Op (Select_f)
{
i32 condition = (i32) _r0;
f64 operand2 = * (f64 *) (_sp + immediate (i32));
f64 operand1 = * (f64 *) (_sp + immediate (i32));
_fp0 = (condition) ? operand1 : operand2;
return nextOp ();
}
d_m3Op (Return)
{
return 0;
}
d_m3Op (Branch)
{
return d_call (* _pc);
}
d_m3Op (Bridge)
{
return d_call (* _pc);
}
d_m3Op (BranchIf)
{
i32 condition = (i32) _r0;
pc_t branch = immediate (pc_t);
if (condition)
{
return d_call (branch);
}
else return nextOp ();
}
d_m3Op (BranchTable)
{
i32 index = (i32) _r0;
u32 numTargets = immediate (u32);
pc_t * branches = (pc_t *) _pc;
if (index < 0 or index > numTargets)
index = numTargets; // the default index
return d_call (branches [index]);
}
d_m3Op (ContinueLoop)
{
// TODO: this is where execution can "escape" the M3 code and callback to the client / fiber switch
// OR it can go in the Loop operation
void * loopId = immediate (void *);
return loopId;
}
d_m3Op (ContinueLoopIf)
{
i32 condition = (i32) _r0;
void * loopId = immediate (void *);
if (condition)
{
return loopId;
}
else return nextOp ();
}
d_m3OpDecl (Compile)
d_m3OpDecl (Call)
d_m3OpDecl (CallIndirect)
d_m3OpDecl (Entry)
d_m3Op (Const)
{
u64 constant = immediate (u64);
i32 offset = immediate (i32);
* (_sp + offset) = constant;
return nextOp ();
}
d_m3OpDecl (Trap)
d_m3OpDecl (End)
d_m3Op (GetGlobal)
{
i64 * global = immediate (i64 *);
// printf ("get global: %p %lld\n", global, *global);
i32 offset = immediate (i32);
* (_sp + offset) = * global;
return nextOp ();
}
d_m3Op (SetGlobal_s)
{
i64 * global = immediate (i64 *);
i32 offset = immediate (i32);
* global = * (_sp + offset);
return nextOp ();
}
d_m3Op (SetGlobal_i)
{
i64 * global = immediate (i64 *);
* global = _r0;
// printf ("set global: %p %lld\n", global, _r0);
return nextOp ();
}
d_m3Op (SetGlobal_f)
{
f64 * global = immediate (f64 *);
* global = _fp0;
return nextOp ();
}
d_m3Op (CopySlot)
{
u64 * dst = _sp + immediate (i32);
u64 * src = _sp + immediate (i32);
* dst = * src; // printf ("copy: %p <- %lld <- %p\n", dst, * dst, src);
return nextOp ();
}
d_m3Op (PreserveCopySlot)
{
u64 * dest = _sp + immediate (i32);
u64 * src = _sp + immediate (i32);
u64 * preserve = _sp + immediate (i32);
* preserve = * dest;
* dest = * src;
return nextOp ();
}
d_m3Op (SetRegister_i)
{
i32 offset = immediate (i32);
u64 * stack = _sp + offset;
_r0 = * stack;
return nextOp ();
}
d_m3Op (SwapRegister_i)
{
slot (u64) = _r0;
_r0 = slot (u64);
return nextOp ();
}
d_m3Op (SetRegister_f)
{
i32 offset = immediate (i32);
f64 * stack = (f64 *) _sp + offset;
_fp0 = * stack;
return nextOp ();
}
d_m3Op (SetSlot_i)
{
i32 offset = immediate (i32);
// printf ("setslot_i %d\n", offset);
u64 * stack = _sp + offset;
* stack = _r0;
return nextOp ();
}
d_m3Op (PreserveSetSlot_i)
{
u64 * stack = (u64 *) _sp + immediate (i32);
u64 * preserve = (u64 *) _sp + immediate (i32);
* preserve = * stack;
* stack = _r0;
return nextOp ();
}
d_m3Op (SetSlot_f)
{
i32 offset = immediate (i32);
f64 * stack = (f64 *) _sp + offset;
* stack = _fp0;
return nextOp ();
}
d_m3Op (PreserveSetSlot_f)
{
f64 * stack = (f64 *) _sp + immediate (i32);
f64 * preserve = (f64 *) _sp + immediate (i32);
* preserve = * stack;
* stack = _fp0;
return nextOp ();
}
#define d_m3Load(REG,DEST,SRC) static inline m3ret_t vectorcall op_##DEST##_Load_##SRC##_r (d_m3OpSig) \
{ \
u32 offset = immediate (u32); \
u32 operand = (u32) _r0; \
SRC * source = (SRC *) (_mem + operand + offset); \
REG = (DEST) * source; \
return nextOp (); \
} \
static inline m3ret_t vectorcall op_##DEST##_Load_##SRC##_s (d_m3OpSig) \
{ \
u32 operand = * (u32 *) (_sp + immediate (i32)); \
u32 offset = immediate (u32); \
SRC * source = (SRC *) (_mem + operand + offset); \
REG = (DEST) * source; \
return nextOp (); \
}
// printf ("get: %d -> %d\n", operand + offset, (i64) REG); \
#define d_m3Load_i(DEST, SRC) d_m3Load(_r0, DEST, SRC)
d_m3Load_i (i32, i32);
d_m3Load_i (i32, i8);
d_m3Load_i (i32, u8);
d_m3Load_i (i32, i16);
d_m3Load_i (i32, u16);
d_m3Op (f64_Store)
{
u32 offset = immediate (u32);
u32 operand = (u32) _r0; // printf ("store: %d\n", operand);
u8 * mem8 = (u8 *) (_mem + operand + offset);
* (f64 *) mem8 = _fp0;
return nextOp ();
}
//#define d_outOfBounds return c_m3Err_trapOutOfBoundsMemoryAccess
#define d_outOfBounds { printf ("%p %p\n", mem8, end); return c_m3Err_trapOutOfBoundsMemoryAccess; }
#define d_m3Store_i(SRC_TYPE, SIZE_TYPE) \
d_m3Op (SRC_TYPE##_Store_##SIZE_TYPE##_sr) \
{ \
u32 operand = * (u32 *) (_sp + immediate (i32)); \
\
u32 offset = immediate (u32); \
operand += offset; \
\
u8 * end = * ((u8 **) _mem - 1); \
u8 * mem8 = (u8 *) (_mem + operand); \
\
if (mem8 + sizeof (SIZE_TYPE) <= end) \
{ \
* (SIZE_TYPE *) mem8 = (SIZE_TYPE) _r0; \
return nextOp (); \
} \
else d_outOfBounds; \
} \
d_m3Op (SRC_TYPE##_Store_##SIZE_TYPE##_rs) \
{ \
u32 operand = (u32) _r0; \
SRC_TYPE value = * (SRC_TYPE *) (_sp + immediate (i32));\
\
u32 offset = immediate (u32); \
operand += offset; \
\
u8 * end = * ((u8 **) _mem - 1); \
u8 * mem8 = (u8 *) (_mem + operand); \
\
if (mem8 + sizeof (SIZE_TYPE) <= end) \
{ \
* (SIZE_TYPE *) mem8 = value; \
return nextOp (); \
} \
else d_outOfBounds; \
} \
d_m3Op (SRC_TYPE##_Store_##SIZE_TYPE##_ss) \
{ \
i32 slot = immediate (i32); \
SRC_TYPE value = * (SRC_TYPE *) (_sp + slot); \
\
u32 operand = * (u32 *) (_sp + immediate (i32)); \
u32 offset = immediate (u32); \
operand += offset; \
\
u8 * end = * ((u8 **) _mem - 1); \
u8 * mem8 = (u8 *) (_mem + operand); \
\
if (mem8 + sizeof (SIZE_TYPE) <= end) \
{ \
* (SIZE_TYPE *) mem8 = value; \
return nextOp (); \
} \
else d_outOfBounds; \
}
d_m3Store_i (i32, u8)
d_m3Store_i (i32, i32)
d_m3Store_i (i32, i16)
//---------------------------------------------------------------------------------------------------------------------
# if d_m3EnableOptimizations
//---------------------------------------------------------------------------------------------------------------------
#define d_m3BinaryOpWith1_i(TYPE, NAME, OPERATION) d_m3RetSig op_##TYPE##_##NAME (d_m3OpSig) \
{ \
_r0 = _r0 OPERATION 1; \
return nextOp (); \
}
d_m3BinaryOpWith1_i (u64, Increment, +)
d_m3BinaryOpWith1_i (u32, Decrement, -)
d_m3BinaryOpWith1_i (u32, ShiftLeft1, <<)
d_m3BinaryOpWith1_i (u64, ShiftLeft1, <<)
d_m3BinaryOpWith1_i (u32, ShiftRight1, >>)
d_m3BinaryOpWith1_i (u64, ShiftRight1, >>)
//---------------------------------------------------------------------------------------------------------------------
# endif
#endif /* m3_exec_h */

@ -0,0 +1,46 @@
//
// m3_exec_defs.h
// m3
//
// Created by Steven Massey on 5/1/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef m3_exec_defs_h
#define m3_exec_defs_h
#include "m3_core.h"
// default Windows x64 calling convention doesn't have enough registers for M3. It only supports
// 4 args passed through registers but its enhanced __vectorcall calling convention does.
// I've yet to test M3 on Windows though...
# if defined (_WIN32) || defined (WIN32)
# define vectorcall __vectorcall
#
# else
# define vectorcall
# endif
typedef f64 arch_f;
typedef i64 arch_i;
//---------------------------------------------------------------------------------------------------------------
static const i64 c_m3NumIntRegisters = 1;
static const i64 c_m3NumFpRegisters = 1;
# define d_m3OpSig pc_t _pc, u64 * _sp, u8 * _mem, m3reg_t _r0, f64 _fp0
# define d_m3OpArgs _sp, _mem, _r0, _fp0
# define d_m3OpAllArgs _pc, _sp, _mem, _r0, _fp0
# define d_m3OpDefaultArgs 666, NAN
static const i64 c_m3NumRegisters = c_m3NumIntRegisters + c_m3NumFpRegisters;
typedef m3ret_t (* IM3Operation) (d_m3OpSig);
#endif /* m3_exec_defs_h */

@ -0,0 +1,181 @@
//
// m3_host.c
// m3
//
// Created by Steven Massey on 4/28/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include "m3_host.h"
#include "m3_core.h"
#include <stdio.h>
#include "m3_env.h"
void m3_printf (cstr_t i_format, const void * i_varArgs)
{
const size_t c_bufferLength = 1000;
char format [c_bufferLength];
char output [c_bufferLength];
size_t formatLength = strlen (i_format) + 1;
char * buffer = formatLength <= c_bufferLength ? format : malloc (formatLength);
size_t numArgs = 0;
char * p = 0;
if (buffer)
{
memcpy (buffer, i_format, formatLength);
char * p = buffer + formatLength - 1;
// while (p >= buffer)
// {
// if (*p == '%')
// {
//
// }
// }
// cstr_t f = i_format;
// while (* f)
// {
// if (* f == '%')
// ++argCount;
// ++f;
// }
// printf (i_format, i_varArgs [0], i_varArgs [0]);
// printf ("printf!!!!\n");
printf (format);
if (buffer != format)
free (buffer);
}
}
void m3_abort (i32 i_dunno)
{
// FIX: return trap
abort ();
}
i32 AllocateHeap (M3Memory * io_memory, i32 i_size)
{
i_size = (i_size + 7) & ~7;
size_t ptrOffset = io_memory->heapOffset + (io_memory->heapAllocated += i_size);
return (i32) ptrOffset;
}
i32 m3_malloc (IM3Module i_module, i32 i_size)
{
i32 heapOffset = AllocateHeap (& i_module->memory, i_size);
printf ("malloc module: %s size: %d off: %d %p\n", i_module->name, i_size, heapOffset, i_module->memory.wasmPages + heapOffset);
return heapOffset;
}
void m3_free (IM3Module i_module, i32 i_data)
{
printf ("malloc free: %s\n", i_module->name);
}
void * m3_memset (void * i_ptr, i32 i_value, i32 i_size)
{
memset (i_ptr, i_value, i_size);
return i_ptr;
}
i32 m3_fopen (IM3Module i_module, ccstr_t i_path, ccstr_t i_mode)
{
i32 offset = 0;
printf ("fopen: %s '%s'\n", i_path, i_mode);
FILE * file = fopen (i_path, i_mode);
if (file)
{
offset = AllocateHeap (& i_module->memory, sizeof (FILE *));
void ** ptr = (void **) (i_module->memory.wasmPages + offset);
* ptr = file;
}
return offset;
}
// TODO: system calls should be able to return traps. make return first arg.
i32 m3_fread (void * io_ptr, i32 i_size, i32 i_count, FILE * i_file)
{
FILE * file = * (void **) i_file;
return (i32) fread (io_ptr, i_size, i_count, file);
}
M3Result EmbedHost (IM3Runtime i_runtime)
{
M3Result result = c_m3Err_none;
return result;
}
double TestReturn (int32_t i_value)
{
return i_value / 10.;
}
void m3StdOut (const char * const i_string)
{
printf ("m3_out: %s", i_string);
}
void m3Out_f64 (double i_value)
{
printf ("%lf\n", i_value);
}
void m3Out_i32 (i32 i_value)
{
printf ("m3_out: %d %u\n", i_value, (u32) i_value);
}
void m3TestOut (int32_t i_int0, double i_double, int32_t i_int1)
{
printf ("0: %d, 1: %lf, 2: %d\n", i_int0, i_double, i_int1);
}
void m3Export (const void * i_data, i32 i_size)
{
f64 v = * (f64 *) i_data;
// printf ("%lf\n", v);
printf ("exporting: %p %d bytes\n", i_data, i_size);
FILE * f = fopen ("wasm.mandel.ppm", "wb");
const char * header = "P6\n1200 800\n255\n";
fwrite (header, 1, strlen (header), f);
fwrite (i_data, 1, i_size, f);
fclose (f);
}

@ -0,0 +1,43 @@
//
// m3_host.h
// m3
//
// Created by Steven Massey on 4/28/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef m3_host_h
#define m3_host_h
//#include <inttypes.h>
#include "m3_core.h"
# if __cplusplus
extern "C" {
# endif
void m3StdOut (const char * const i_string);
void m3Export (const void * i_data, int32_t i_size);
double TestReturn (int32_t);
void m3Out_f64 (double i_value);
void m3Out_i32 (int32_t i_value);
void m3TestOut (int32_t i_int, double i_double, int32_t i_int1);
void m3_printf (cstr_t i_format, const void * i_varArgs);
void m3_abort ();
i32 m3_malloc (IM3Module i_module, i32 i_size);
void m3_free (IM3Module i_module, i32 i_data);
void * m3_memset (void * i_ptr, i32 i_value, i32 i_size);
i32 m3_fread (void * ptr, i32 size, i32 count, FILE * stream);
i32 m3_fopen (IM3Module i_module, ccstr_t i_path, ccstr_t i_mode);
# if __cplusplus
}
# endif
#endif /* m3_host_h */

@ -0,0 +1,223 @@
//
// m3_info.c
// m3
//
// Created by Steven Massey on 4/27/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include "m3_env.h"
#include "m3_compile.h"
#include <assert.h>
void m3_PrintM3Info ()
{
printf ("\n-- m3 configuration --------------------------------------------\n");
// printf (" sizeof M3CodePage : %lu bytes (%d slots) \n", sizeof (M3CodePage), c_m3CodePageNumSlots);
printf (" sizeof M3MemPage : %d bytes \n", c_m3MemPageSize);
printf (" sizeof M3Compilation : %ld bytes \n", sizeof (M3Compilation));
printf ("----------------------------------------------------------------\n\n");
}
void * v_PrintEnvModuleInfo (IM3Module i_module, u32 * io_index)
{
printf (" module [%u] name: '%s'; funcs: %d \n", * io_index++, i_module->name, i_module->numFunctions);
return NULL;
}
void m3_PrintRuntimeInfo (IM3Runtime i_runtime)
{
printf ("\n-- m3 runtime -------------------------------------------------\n");
printf (" stack-size: %lu \n\n", i_runtime->numStackSlots * sizeof (m3word_t));
u32 moduleIndex = 0;
ForEachModule (i_runtime, (ModuleVisitor) v_PrintEnvModuleInfo, & moduleIndex);
printf ("----------------------------------------------------------------\n\n");
}
cstr_t GetTypeName (u8 i_m3Type)
{
if (i_m3Type < 7)
return c_waTypes [i_m3Type];
else
return "?";
}
void PrintFuncTypeSignature (IM3FuncType i_funcType)
{
printf ("(");
u32 numArgs = i_funcType->numArgs;
u8 * types = i_funcType->argTypes;
for (u32 i = 0; i < numArgs; ++i)
{
if (i != 0)
printf (", ");
printf ("%s", GetTypeName (types [i]));
}
printf (") -> %s", GetTypeName (i_funcType->returnType));
}
size_t SPrintArg (char * o_string, size_t i_n, m3stack_t i_sp, u8 i_type)
{
size_t len = 0;
if (i_type == c_m3Type_i32)
len = snprintf (o_string, i_n, "%d", * (i32 *) i_sp);
else if (i_type == c_m3Type_i64)
len = snprintf (o_string, i_n, "%lld", * i_sp);
else if (i_type == c_m3Type_f32)
len = snprintf (o_string, i_n, "%f", * (f32 *) i_sp);
else if (i_type == c_m3Type_f64)
len = snprintf (o_string, i_n, "%lf", * (f64 *) i_sp);
len = max (0, len);
return len;
}
ccstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp)
{
const u32 bufferSize = 1000;
static char string [bufferSize];
char * s = string;
ccstr_t e = string + bufferSize - 1;
s += max (0, snprintf (s, e-s, "("));
IM3FuncType funcType = i_function->funcType;
if (funcType)
{
u32 numArgs = funcType->numArgs;
u8 * types = funcType->argTypes;
for (u32 i = 0; i < numArgs; ++i)
{
u8 type = types [i];
s += max (0, snprintf (s, e-s, "%s: ", c_waTypes [type]));
s += SPrintArg (s, e-s, i_sp + i, type);
if (i != numArgs - 1)
s += max (0, snprintf (s, e-s, ", "));
}
}
else printf ("null signature");
s += max (0, snprintf (s, e-s, ")"));
return string;
}
typedef struct OpInfo
{
IM3OpInfo info;
u8 opcode;
}
OpInfo;
OpInfo FindOperationInfo (IM3Operation i_operation)
{
OpInfo opInfo = {};
for (u32 i = 0; i <= 0xff; ++i)
{
IM3OpInfo oi = & c_operations [i];
if (oi->name)
{
if (oi->operation_rs == i_operation or
oi->operation_sr == i_operation or
oi->operation_ss == i_operation)
{
opInfo.info = oi;
opInfo.opcode = i;
break;
}
}
else break;
}
return opInfo;
}
void DecodeOperation (char * o_string, u8 i_opcode, IM3OpInfo i_opInfo, pc_t * o_pc)
{
pc_t pc = * o_pc;
#undef fetch
#define fetch(TYPE) (*(TYPE *) (pc++))
i32 offset;
u64 value;
switch (i_opcode)
{
case 0xbf+1:
{
value = fetch (u64); offset = fetch (i32);
sprintf (o_string, " slot [%d] = %llu", offset, value);
break;
}
}
#undef fetch
* o_pc = pc;
}
void DumpCodePage (IM3CodePage i_codePage, pc_t i_startPC)
{
if (d_m3LogCodePages)
{ m3log (code, "code page seq: %d", i_codePage->info.sequence);
pc_t pc = i_startPC ? i_startPC : (pc_t) i_codePage;
pc_t end = GetPagePC (i_codePage);
m3log (code, "---------------------------------------------------");
while (pc < end)
{
IM3Operation op = * pc++;
if (op)
{
OpInfo i = FindOperationInfo (op);
if (i.info)
{
char infoString [1000] = { 0 };
DecodeOperation (infoString, i.opcode, i.info, & pc);
m3log (code, "%p: %15s %-20s", pc - 1, i.info->name, infoString);
}
else break;
}
else break;
}
m3log (code, "---------------------------------------------------");
}
}

@ -0,0 +1,159 @@
//
// m3_module.c
// m3
//
// Created by Steven Massey on 5/7/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include "m3_module.h"
void m3_FreeModule (IM3Module i_module)
{
if (i_module)
{
m3log (module, "freeing module: %s (funcs: %d; segments: %d)",
i_module->name, i_module->numFunctions, i_module->numDataSegments);
free (i_module->functions);
free (i_module->imports);
free (i_module->funcTypes);
free (i_module->dataSegments);
// TODO: free importinfo
free (i_module->globals);
free (i_module);
}
}
M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_type, bool i_mutable, bool i_isImported)
{
M3Result result = c_m3Err_none;
u32 index = io_module->numGlobals++;
io_module->globals = m3RellocArray (io_module->globals, M3Global, io_module->numGlobals, index);
if (io_module->globals)
{
M3Global * global = & io_module->globals [index];
global->type = i_type;
global->imported = i_isImported;
if (o_global)
* o_global = global;
}
else result = c_m3Err_mallocFailed;
return result;
}
M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo)
{
M3Result result = c_m3Err_none;
u32 index = io_module->numFunctions++;
io_module->functions = m3RellocArray (io_module->functions, M3Function, io_module->numFunctions, index);
if (io_module->functions)
{
if (i_typeIndex < io_module->numFuncTypes)
{
IM3FuncType ft = & io_module->funcTypes [i_typeIndex];
IM3Function func = Module_GetFunction (io_module, index);
func->funcType = ft;
if (i_importInfo)
{
func->import = * i_importInfo;
func->name = i_importInfo->fieldUtf8;
}
// m3log (module, " added function: %3d; sig: %d", index, i_typeIndex);
}
else result = "unknown type sig index";
}
else result = c_m3Err_mallocFailed;
return result;
}
IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex)
{
IM3Function func = NULL;
if (i_functionIndex < i_module->numFunctions)
func = & i_module->functions [i_functionIndex];
return func;
}
M3Result Module_EnsureMemorySize (IM3Module i_module, M3Memory * io_memory, m3word_t i_memorySize)
{
M3Result result = c_m3Err_none;
if (i_memorySize <= io_memory->virtualSize)
{
size_t actualSize = 0;
if (io_memory->mallocated)
actualSize = (u8 *) io_memory->mallocated->end - (u8 *) io_memory->mallocated;
if (i_memorySize > actualSize)
{
i_memorySize = io_memory->virtualSize; // hack
// m3word_t alignedSize = i_memorySize + sizeof (void *) * 2; // end pointer + module ptr
// HACK: this is all hacked 'cause I don't understand the Wasm memory. Or it doesn't understand me.
// Just get'n some tests/benchmarks going for now:
i32 pages = 2;
size_t extra = c_m3MemPageSize * pages + 900000 * 2 + sizeof (M3MemoryHeader);
m3word_t alignedSize = i_memorySize + extra;
if (c_m3AlignWasmMemoryToPages)
{
m3word_t aligner = c_m3MemPageSize - 1;
alignedSize += aligner;
alignedSize &= ~aligner;
}
io_memory->mallocated = m3Realloc (io_memory->mallocated, alignedSize, actualSize);
m3log (runtime, "resized WASM linear memory to %llu bytes (%p)", alignedSize, io_memory->mallocated);
if (io_memory->mallocated)
{
void * end = (u8 *) io_memory->mallocated + alignedSize;
u8 * ptr = (u8 *) (io_memory->mallocated + 1);
io_memory->wasmPages = ptr;
// store pointer to module and end of memory. gives the runtime access to this info.
io_memory->mallocated->module = i_module;
io_memory->mallocated->end = end;
// printf ("start= %p end= %p \n", ptr, end);
io_memory->heapOffset = i_memorySize;
}
else result = c_m3Err_mallocFailed;
}
}
else result = c_m3Err_wasmMemoryOverflow;
return result;
}

@ -0,0 +1,16 @@
//
// m3_module.h
// m3
//
// Created by Steven Massey on 7/7/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#ifndef m3_module_h
#define m3_module_h
#include "m3_env.h"
M3Result Module_EnsureMemorySize (IM3Module i_module, M3Memory * io_memory, m3word_t i_memorySize);
#endif /* m3_module_h */

@ -0,0 +1,32 @@
//
// m3_optimize.c
// m3
//
// Created by Steven Massey on 4/27/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include "m3_compile.h"
#include "m3_exec.h"
// not currently used now.
bool PeekNextOpcode (IM3Compilation o, u8 i_opcode)
{
bool found = false;
if (o->wasm < o->wasmEnd)
{
u8 opcode = * o->wasm;
if (opcode == i_opcode)
{
found = true;
o->wasm++;
}
}
return found;
}

@ -0,0 +1,542 @@
//
// m3_parse.c
// M3: Massey Meta Machine
//
// Created by Steven Massey on 4/19/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include "m3_env.h"
#include "m3_compile.h"
#include "m3_exec.h"
#include "m3_exception.h"
M3Result ParseType_Table (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_none;
return result;
}
M3Result ParseSection_Type (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_none;
u32 numTypes;
_ (ReadLEB_u32 (& numTypes, & i_bytes, i_end)); m3log (parse, "** Type [%d]", numTypes);
if (numTypes)
{
_ (m3Alloc (& io_module->funcTypes, M3FuncType, numTypes));
io_module->numFuncTypes = numTypes;
IM3FuncType ft = io_module->funcTypes;
while (numTypes--)
{
i8 form;
_ (ReadLEB_i7 (& form, & i_bytes, i_end));
if (form != -32)
throw (c_m3Err_wasmMalformed); // for WA MVP }
_ (ReadLEB_u32 (& ft->numArgs, & i_bytes, i_end));
if (ft->numArgs <= c_m3MaxNumFunctionArgs)
{
for (u32 i = 0; i < ft->numArgs; ++i)
{
i8 argType;
_ (ReadLEB_i7 (& argType, & i_bytes, i_end));
ft->argTypes [i] = -argType;
}
}
else throw (c_m3Err_typeListOverflow);
u8 returnCount;
_ (ReadLEB_u7 /* u1 in spec */ (& returnCount, & i_bytes, i_end));
if (returnCount)
{
i8 returnType;
_ (ReadLEB_i7 (& returnType, & i_bytes, i_end));
_ (NormalizeType (& ft->returnType, returnType));
} m3logif (parse, PrintFuncTypeSignature (ft))
++ft;
}
}
catch:
if (result)
{
free (io_module->funcTypes);
io_module->funcTypes = NULL;
io_module->numFuncTypes = 0;
}
return result;
}
M3Result ParseSection_Function (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_none;
u32 numFunctions;
_ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Function [%d]", numFunctions);
for (u32 i = 0; i < numFunctions; ++i)
{
u32 funcTypeIndex;
_ (ReadLEB_u32 (& funcTypeIndex, & i_bytes, i_end));
_ (Module_AddFunction (io_module, funcTypeIndex, NULL /* import info */));
}
catch: return result;
}
M3Result ParseSection_Import (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_none;
M3ImportInfo import = {}, clearImport = {};
u32 numImports;
_ (ReadLEB_u32 (& numImports, & i_bytes, i_end)); m3log (parse, "** Import [%d]", numImports);
for (u32 i = 0; i < numImports; ++i)
{
u8 importKind;
_ (Read_utf8 (& import.moduleUtf8, & i_bytes, i_end));
_ (Read_utf8 (& import.fieldUtf8, & i_bytes, i_end));
_ (Read_u8 (& importKind, & i_bytes, i_end)); m3log (parse, " - kind: %d; '%s.%s' ",
(u32) importKind, import.moduleUtf8, import.fieldUtf8);
switch (importKind)
{
case c_externalKind_function:
{
u32 typeIndex;
_ (ReadLEB_u32 (& typeIndex, & i_bytes, i_end))
_ (Module_AddFunction (io_module, typeIndex, & import))
import = clearImport;
io_module->numImports++;
}
break;
case c_externalKind_table:
// abort ();
// result = ParseType_Table (& i_bytes, i_end);
break;
case c_externalKind_memory:
{
u8 flag;
u32 pages, maxPages = 0;
_ (ReadLEB_u7 (& flag, & i_bytes, i_end)); // really a u1
_ (ReadLEB_u32 (& pages, & i_bytes, i_end));
if (flag)
_ (ReadLEB_u32 (& maxPages, & i_bytes, i_end));
io_module->memory.virtualSize = pages * c_m3MemPageSize; m3log (parse, " memory: pages: %d max: %d", pages, maxPages);
}
break;
case c_externalKind_global:
{
i8 waType;
u8 type, mutable;
_ (ReadLEB_i7 (& waType, & i_bytes, i_end));
_ (NormalizeType (& type, waType));
_ (ReadLEB_u7 (& mutable, & i_bytes, i_end)); m3log (parse, " global: %s mutable=%d", c_waTypes [type], (u32) mutable);
IM3Global global;
_ (Module_AddGlobal (io_module, & global, type, mutable, true /* isImport */));
global->import = import;
import = clearImport;
}
break;
default:
throw (c_m3Err_wasmMalformed);
}
FreeImportInfo (& import);
}
catch:
{
FreeImportInfo (& import);
}
return result;
}
M3Result ParseSection_Export (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_none;
u32 numExports;
_ (ReadLEB_u32 (& numExports, & i_bytes, i_end)); m3log (parse, "** Export [%d]", numExports);
for (u32 i = 0; i < numExports; ++i)
{
const char * utf8;
u8 exportKind;
u32 index;
_ (Read_utf8 (& utf8, & i_bytes, i_end));
_ (Read_u8 (& exportKind, & i_bytes, i_end));
_ (ReadLEB_u32 (& index, & i_bytes, i_end)); m3log (parse, " - index: %4d; kind: %d; export: '%s'; ", index, (u32) exportKind, utf8);
if (exportKind == c_externalKind_function)
{
if (not io_module->functions [index].name)
{
io_module->functions [index].name = utf8;
utf8 = NULL; // ownership transfered to M3Function
}
}
free ((void *) utf8);
}
catch: return result;
}
M3Result Parse_InitExpr (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_none;
// this doesn't generate code pages. just walks the wasm bytecode to find the end
IM3Runtime rt;
M3Compilation compilation = { rt= NULL, io_module, * io_bytes, i_end };
result = Compile_BlockStatements (& compilation);
* io_bytes = compilation.wasm;
return result;
}
M3Result ParseSection_Element (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_none;
u32 numSegments;
result = ReadLEB_u32 (& numSegments, & i_bytes, i_end); m3log (parse, "** Element [%d]", numSegments);
if (not result)
{
io_module->elementSection = i_bytes;
io_module->elementSectionEnd = i_end;
io_module->numElementSegments = numSegments;
}
else result = "error parsing Element section";
return result;
}
M3Result ParseSection_Code (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
{
M3Result result;
u32 numFunctions;
_ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Code [%d]", numFunctions);
if (numFunctions != io_module->numFunctions - io_module->numImports)
{
numFunctions = 0;
throw (c_m3Err_wasmMalformed); // FIX: better error
}
for (u32 f = 0; f < numFunctions; ++f)
{
u32 size;
_ (ReadLEB_u32 (& size, & i_bytes, i_end));
if (size)
{
const u8 * ptr = i_bytes;
i_bytes += size;
if (i_bytes <= i_end)
{
const u8 * start = ptr;
u32 numLocals;
_ (ReadLEB_u32 (& numLocals, & ptr, i_end)); m3log (parse, " - func size: %d; locals: %d", size, numLocals);
u32 numLocalVars = 0;
for (u32 l = 0; l < numLocals; ++l)
{
u32 varCount;
i8 varType;
u8 normalizedType;
_ (ReadLEB_u32 (& varCount, & ptr, i_end));
_ (ReadLEB_i7 (& varType, & ptr, i_end));
_ (NormalizeType (& normalizedType, varType));
numLocalVars += varCount; m3log (parse, " - %d locals; type: '%s'", varCount, c_waTypes [-varType]);
}
IM3Function func = Module_GetFunction (io_module, f + io_module->numImports);
func->module = io_module;
func->wasm = start;
func->wasmEnd = i_bytes;
func->numLocals = numLocalVars;
}
else throw (c_m3Err_wasmSectionOverrun);
}
}
catch:
if (not result and i_bytes != i_end)
result = c_m3Err_wasmSectionUnderrun;
return result;
}
M3Result ParseSection_Data (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_none;
u32 numDataSegments;
_ (ReadLEB_u32 (& numDataSegments, & i_bytes, i_end)); m3log (parse, "** Data [%d]", numDataSegments);
_ (m3Alloc (& io_module->dataSegments, M3DataSegment, numDataSegments));
io_module->numDataSegments = numDataSegments;
for (u32 i = 0; i < numDataSegments; ++i)
{
M3DataSegment * segment = & io_module->dataSegments [i];
_ (ReadLEB_u32 (& segment->memoryRegion, & i_bytes, i_end));
segment->initExpr = i_bytes;
_ (Parse_InitExpr (io_module, & i_bytes, i_end));
segment->initExprSize = (u32) (i_bytes - segment->initExpr);
if (segment->initExprSize <= 1)
throw (c_m3Err_wasmMissingInitExpr);
_ (ReadLEB_u32 (& segment->size, & i_bytes, i_end));
segment->data = i_bytes; m3log (parse, " segment [%u] memory: %u; expr-size: %d; size: %d",
i, segment->memoryRegion, segment->initExprSize, segment->size);
}
catch:
// TODO failure cleanup
return result;
}
M3Result ParseSection_Global (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
{
M3Result result = c_m3Err_none;
u32 numGlobals;
_ (ReadLEB_u32 (& numGlobals, & i_bytes, i_end)); m3log (parse, "** Global [%d]", numGlobals);
for (u32 i = 0; i < numGlobals; ++i)
{
i8 waType;
u8 type;
_ (ReadLEB_i7 (& waType, & i_bytes, i_end));
_ (NormalizeType (& type, waType)); m3log (parse, " - add global: [%d] %s", i, c_waTypes [type]);
IM3Global global;
_ (Module_AddGlobal (io_module, & global, type, false /* mutable */, false /* isImport */));
global->initExpr = i_bytes;
_ (Parse_InitExpr (io_module, & i_bytes, i_end));
global->initExprSize = (u32) (i_bytes - global->initExpr);
if (global->initExprSize <= 1)
throw (c_m3Err_wasmMissingInitExpr);
}
catch: return result;
}
M3Result ParseSection_Custom (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
{
M3Result result;
cstr_t name;
_ (Read_utf8 (& name, & i_bytes, i_end));
m3log (parse, "** Custom: '%s'", name);
if (strcmp (name, "name") != 0)
i_bytes = i_end;
free ((void *) name);
while (i_bytes < i_end)
{
u8 nameType;
u32 payloadLength;
_ (ReadLEB_u7 (& nameType, & i_bytes, i_end));
_ (ReadLEB_u32 (& payloadLength, & i_bytes, i_end));
if (nameType == 1)
{
u32 numNames;
_ (ReadLEB_u32 (& numNames, & i_bytes, i_end));
for (u32 i = 0; i < numNames; ++i)
{
u32 index;
_ (ReadLEB_u32 (& index, & i_bytes, i_end));
_ (Read_utf8 (& name, & i_bytes, i_end));
if (index < io_module->numFunctions)
{
if (not io_module->functions [index].name)
{
io_module->functions [index].name = name; m3log (parse, "naming function [%d]: %s", index, name);
name = NULL;
}
// else m3log (parse, "prenamed: %s", io_module->functions [index].name);
}
free ((void *) name);
}
}
i_bytes += payloadLength;
}
catch: return result;
}
M3Result ParseModuleSection (M3Module * o_module, u8 i_sectionType, bytes_t i_bytes, u32 i_numBytes)
{
M3Result result = c_m3Err_none;
typedef M3Result (* M3Parser) (M3Module *, bytes_t, cbytes_t);
static M3Parser s_parsers [] =
{
ParseSection_Custom, // 0
ParseSection_Type, // 1
ParseSection_Import, // 2
ParseSection_Function, // 3
NULL, // 4: table
NULL, // 5: memory
ParseSection_Global, // 6
ParseSection_Export, // 7
NULL, // 8: start
ParseSection_Element, // 9
ParseSection_Code, // 10
ParseSection_Data // 11
};
M3Parser parser = NULL;
if (i_sectionType <= 11)
parser = s_parsers [i_sectionType];
if (parser)
{
cbytes_t end = i_bytes + i_numBytes;
result = parser (o_module, i_bytes, end);
}
else m3log (parse, "<skipped (id: %d)>", (u32) i_sectionType);
return result;
}
M3Result m3_ParseModule (IM3Module * o_module, cbytes_t i_bytes, u32 i_numBytes)
{
M3Result result;
IM3Module module;
_ (m3Alloc (& module, M3Module, 1));
// Module_Init (module);
module->name = ".unnamed"; m3log (parse, "load module: %d bytes", i_numBytes);
const u8 * pos = i_bytes;
const u8 * end = pos + i_numBytes;
u32 magic = 0;
_ (Read_u32 (& magic, & pos, end));
if (magic == 0x6d736100)
{
u32 version;
_ (Read_u32 (&version, & pos, end));
if (version == 1)
{ m3log (parse, "found magic + version");
u8 previousSection = 0;
while (pos < end)
{
u8 sectionCode;
_ (ReadLEB_u7 (& sectionCode, & pos, end));
if (sectionCode > previousSection or sectionCode == 0) // from the spec: sections must appear in order
{
u32 sectionLength;
_ (ReadLEB_u32 (& sectionLength, & pos, end));
_ (ParseModuleSection (module, sectionCode, pos, sectionLength));
pos += sectionLength;
if (sectionCode)
previousSection = sectionCode;
}
else throw (c_m3Err_misorderedWasmSection);
}
}
else throw (c_m3Err_incompatibleWasmVersion);
}
else throw (c_m3Err_wasmMalformed);
catch:
if (result)
{
m3_FreeModule (module);
module = NULL;
}
* o_module = module;
return result;
}

@ -0,0 +1,129 @@
//
//
// Created by Steven Massey on 4/15/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include "m3.hpp"
#include <time.h>
extern "C"
{
# include "m3_core.h"
}
#include "m3_host.h"
void m3Output (const char * i_string)
{
printf ("%s\n", i_string);
}
int main (int argc, const char * argv [])
{
m3_PrintM3Info ();
if (argc == 2)
{
FILE * f = fopen (argv [1], "rb");
if (f)
{
fseek (f, 0, SEEK_END);
size_t fsize = ftell (f);
fseek (f, 0, SEEK_SET);
if (fsize < 100000)
{
u8 * wasm = (u8 *) malloc (fsize);
if (wasm)
{
fread (wasm, 1, fsize, f);
fclose (f);
IM3Module module;
M3Result result = m3_ParseModule (& module, wasm, (u32) fsize);
if (not result)
{
IM3Runtime env = m3_NewRuntime (32768);
result = m3_LoadModule (env, module);
if (not result)
{
m3_LinkFunction (module, "_printf", "v(**)", (void *) m3_printf);
m3_LinkFunction (module, "_m3TestOut", "v(iFi)", (void *) m3TestOut);
m3_LinkFunction (module, "_m3StdOut", "v(*)", (void *) m3Output);
m3_LinkFunction (module, "_m3Export", "v(*i)", (void *) m3Export);
m3_LinkFunction (module, "_m3Out_f64", "v(F)", (void *) m3Out_f64);
m3_LinkFunction (module, "_m3Out_i32", "v(i)", (void *) m3Out_i32);
m3_LinkFunction (module, "_TestReturn", "F(i)", (void *) TestReturn);
m3_LinkFunction (module, "abortStackOverflow", "v(i)", (void *) m3_abort);
m3_LinkFunction (module, "_malloc", "i(Mi)", (void *) m3_malloc);
m3_LinkFunction (module, "_free", "v(Mi)", (void *) m3_free);
m3_LinkFunction (module, "_memset", "*(*ii)", (void *) m3_memset);
m3_LinkFunction (module, "_fopen", "i(M**)", (void *) m3_fopen);
m3_LinkFunction (module, "_fread", "i(*ii*)", (void *) m3_fread);
m3_PrintRuntimeInfo (env);
IM3Function f;
result = m3_FindFunction (& f, env, "__post_instantiate");
if (not result)
result = m3_Call (f);
IM3Function main;
result = m3_FindFunction (& main, env, "_main");
if (not result and main)
{
printf ("found _main\n");
clock_t start = clock ();
result = m3_Call (main);
clock_t end = clock ();
double elapsed_time = (end - start) / (double) CLOCKS_PER_SEC ;
printf("%lf\n", elapsed_time);
printf ("call: %s\n", result);
m3_PrintProfilerInfo ();
}
else printf ("find: %s\n", result);
}
else printf ("import: %s\n", result);
if (result)
{
M3ErrorInfo info = m3_GetErrorInfo (env);
printf ("%s\n", info.message);
}
m3_FreeRuntime (env);
}
else printf ("parse: %s\n", result);
}
free (wasm);
}
}
else printf ("couldn't open '%s'\n", argv [1]);
}
printf ("\n");
return 0;
}
Loading…
Cancel
Save