realnegate / cuik Goto Github PK
View Code? Open in Web Editor NEWA Modern C11 compiler (STILL EARLY)
License: MIT License
A Modern C11 compiler (STILL EARLY)
License: MIT License
Hello :D
The compile fails when building under musl libc:
ninja: job failed: clang main/main_driver.c -g -march=haswell -I common -Wall -Werror -Wno-unused -Wno-deprecated -DMI_SKIP_COLLECT_ON_EXIT -DCUIK_ALLOW_THREADS -I mimalloc/include -DTB_USE_MIMALLOC -DCUIK_USE_MIMALLOC -DCUIK_USE_CUIK -O2 -DNDEBUG -DLOG_SUPPRESS -D_GNU_SOURCE -I libCuik/include -I tb/include -DCUIK_USE_TB -MD -MF bin/objs/main_driver.o.d -c -o bin/objs/main_driver.o
In file included from main/main_driver.c:16:
main/spall_perf.h:67:14: error: incompatible pointer to integer conversion initializing 'uint32_t' (aka 'unsigned int') with an expression of type 'pthread_t' (aka 'struct __pthread *') [-Wint-conversion]
67 | uint32_t tid = pthread_self();
| ^ ~~~~~~~~~~~~~~
main/spall_perf.h:79:14: error: incompatible pointer to integer conversion initializing 'uint32_t' (aka 'unsigned int') with an expression of type 'pthread_t' (aka 'struct __pthread *') [-Wint-conversion]
79 | uint32_t tid = pthread_self();
| ^ ~~~~~~~~~~~~~~
pthread_self
does not return uint32_t
but pthread_t
which internally is a pointer so i'm concerned with the use of 32bit value on 64bit platforms.
I wrote a naive little patch to fix this:
diff --git a/build.lua b/build.lua
index 8e395dc9..e9c85c28 100644
--- a/build.lua
+++ b/build.lua
@@ -63,7 +63,7 @@ for i = 1, #arg do
end
local ldflags = ""
-local cflags = " -g -march=haswell -I common -Wall -Werror -Wno-unused -Wno-deprecated -DMI_SKIP_COLLECT_ON_EXIT -DCUIK_ALLOW_THREADS -I mimalloc/include"
+local cflags = " -g -march=haswell -I common -Wall -Werror -Wno-unused -Wno-deprecated -DMI_SKIP_COLLECT_ON_EXIT -DCUIK_ALLOW_THREADS -I mimalloc/include -fdiagnostics-color=always"
if options.asan then
cflags = cflags.." -fsanitize=address"
diff --git a/main/spall.h b/main/spall.h
index 724cb4fd..6b37e57d 100644
--- a/main/spall.h
+++ b/main/spall.h
@@ -36,6 +36,7 @@ TODO: Optional Helper APIs:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
+#include <pthread.h>
#define SPALL_FN static inline SPALL_NOINSTRUMENT
@@ -67,9 +68,9 @@ typedef struct SpallBeginEvent {
uint8_t type; // = SpallEventType_Begin
uint8_t category;
- uint32_t pid;
- uint32_t tid;
- double when;
+ uint32_t pid;
+ pthread_t tid;
+ double when;
uint8_t name_length;
uint8_t args_length;
@@ -82,10 +83,10 @@ typedef struct SpallBeginEventMax {
} SpallBeginEventMax;
typedef struct SpallEndEvent {
- uint8_t type; // = SpallEventType_End
- uint32_t pid;
- uint32_t tid;
- double when;
+ uint8_t type; // = SpallEventType_End
+ uint32_t pid;
+ pthread_t tid;
+ double when;
} SpallEndEvent;
typedef struct SpallPadSkipEvent {
@@ -255,7 +256,7 @@ SPALL_FN size_t spall_build_header(void *buffer, size_t rem_size, double timesta
header->must_be_0 = 0;
return header_size;
}
-SPALL_FN SPALL_FORCEINLINE size_t spall_build_begin(void *buffer, size_t rem_size, const char *name, signed long name_len, const char *args, signed long args_len, double when, uint32_t tid, uint32_t pid) {
+SPALL_FN SPALL_FORCEINLINE size_t spall_build_begin(void *buffer, size_t rem_size, const char *name, signed long name_len, const char *args, signed long args_len, double when, pthread_t tid, uint32_t pid) {
SpallBeginEventMax *ev = (SpallBeginEventMax *)buffer;
uint8_t trunc_name_len = (uint8_t)SPALL_MIN(name_len, 255); // will be interpreted as truncated in the app (?)
uint8_t trunc_args_len = (uint8_t)SPALL_MIN(args_len, 255); // will be interpreted as truncated in the app (?)
@@ -277,7 +278,7 @@ SPALL_FN SPALL_FORCEINLINE size_t spall_build_begin(void *buffer, size_t rem_siz
return ev_size;
}
-SPALL_FN SPALL_FORCEINLINE size_t spall_build_end(void *buffer, size_t rem_size, double when, uint32_t tid, uint32_t pid) {
+SPALL_FN SPALL_FORCEINLINE size_t spall_build_end(void *buffer, size_t rem_size, double when, pthread_t tid, uint32_t pid) {
size_t ev_size = sizeof(SpallEndEvent);
if (ev_size > rem_size) {
return 0;
@@ -352,7 +353,7 @@ SPALL_FN bool spall_flush(SpallProfile *ctx) {
return true;
}
-SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_args(SpallProfile *ctx, SpallBuffer *wb, const char *name, signed long name_len, const char *args, signed long args_len, double when, uint32_t tid, uint32_t pid) {
+SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_args(SpallProfile *ctx, SpallBuffer *wb, const char *name, signed long name_len, const char *args, signed long args_len, double when, pthread_t tid, uint32_t pid) {
#ifdef SPALL_DEBUG
if (!ctx) return false;
if (!name) return false;
@@ -363,8 +364,8 @@ SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_args(SpallProfile *ctx, Spall
if (ctx->is_json) {
char buf[1024];
int buf_len = snprintf(buf, sizeof(buf),
- "{\"ph\":\"B\",\"ts\":%f,\"pid\":%u,\"tid\":%u,\"name\":\"%.*s\",\"args\":\"%.*s\"},\n",
- when * ctx->timestamp_unit, pid, tid, (int)(uint8_t)name_len, name, (int)(uint8_t)args_len, args);
+ "{\"ph\":\"B\",\"ts\":%f,\"pid\":%u,\"tid\":%zu,\"name\":\"%.*s\",\"args\":\"%.*s\"},\n",
+ when * ctx->timestamp_unit, pid, (uintptr_t)tid, (int)(uint8_t)name_len, name, (int)(uint8_t)args_len, args);
if (buf_len <= 0) return false;
if (buf_len >= sizeof(buf)) return false;
if (!spall__buffer_write(ctx, wb, buf, buf_len)) return false;
@@ -381,7 +382,7 @@ SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_args(SpallProfile *ctx, Spall
return true;
}
-SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_ex(SpallProfile *ctx, SpallBuffer *wb, const char *name, signed long name_len, double when, uint32_t tid, uint32_t pid) {
+SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_ex(SpallProfile *ctx, SpallBuffer *wb, const char *name, signed long name_len, double when, pthread_t tid, uint32_t pid) {
return spall_buffer_begin_args(ctx, wb, name, name_len, "", 0, when, tid, pid);
}
@@ -389,7 +390,7 @@ SPALL_FN bool spall_buffer_begin(SpallProfile *ctx, SpallBuffer *wb, const char
return spall_buffer_begin_args(ctx, wb, name, name_len, "", 0, when, 0, 0);
}
-SPALL_FN SPALL_FORCEINLINE bool spall_buffer_end_ex(SpallProfile *ctx, SpallBuffer *wb, double when, uint32_t tid, uint32_t pid) {
+SPALL_FN SPALL_FORCEINLINE bool spall_buffer_end_ex(SpallProfile *ctx, SpallBuffer *wb, double when, pthread_t tid, uint32_t pid) {
#ifdef SPALL_DEBUG
if (!ctx) return false;
if (!wb) return false;
@@ -398,8 +399,8 @@ SPALL_FN SPALL_FORCEINLINE bool spall_buffer_end_ex(SpallProfile *ctx, SpallBuff
if (ctx->is_json) {
char buf[512];
int buf_len = snprintf(buf, sizeof(buf),
- "{\"ph\":\"E\",\"ts\":%f,\"pid\":%u,\"tid\":%u},\n",
- when * ctx->timestamp_unit, pid, tid);
+ "{\"ph\":\"E\",\"ts\":%f,\"pid\":%u,\"tid\":%zu},\n",
+ when * ctx->timestamp_unit, pid, (uintptr_t)tid);
if (buf_len <= 0) return false;
if (buf_len >= sizeof(buf)) return false;
if (!spall__buffer_write(ctx, wb, buf, buf_len)) return false;
@@ -423,8 +424,8 @@ SPALL_FN SPALL_FORCEINLINE void spall__buffer_profile(SpallProfile *ctx, SpallBu
// precon: ctx->write
char temp_buffer_data[2048];
SpallBuffer temp_buffer = { temp_buffer_data, sizeof(temp_buffer_data) };
- if (!spall_buffer_begin_ex(ctx, &temp_buffer, name, name_len, spall_time_begin, (uint32_t)(uintptr_t)wb->data, 4222222222)) return;
- if (!spall_buffer_end_ex(ctx, &temp_buffer, spall_time_end, (uint32_t)(uintptr_t)wb->data, 4222222222)) return;
+ if (!spall_buffer_begin_ex(ctx, &temp_buffer, name, name_len, spall_time_begin, (pthread_t)wb->data, 4222222222)) return;
+ if (!spall_buffer_end_ex(ctx, &temp_buffer, spall_time_end, (pthread_t)wb->data, 4222222222)) return;
if (ctx->write) ctx->write(ctx, temp_buffer_data, temp_buffer.head);
}
diff --git a/main/spall_perf.h b/main/spall_perf.h
index f9775b76..dd1d14c9 100644
--- a/main/spall_perf.h
+++ b/main/spall_perf.h
@@ -64,7 +64,7 @@ static void spallperf__begin_plot(void* user_data, uint64_t nanos, const char* l
#if _WIN32
uint32_t tid = GetCurrentThreadId();
#else
- uint32_t tid = pthread_self();
+ pthread_t tid = pthread_self();
#endif
spall_buffer_begin_args(&ctx, &muh_buffer, label, strlen(label), extra, strlen(extra), nanos, tid, 0);
@@ -76,7 +76,7 @@ static void spallperf__end_plot(void* user_data, uint64_t nanos) {
#if _WIN32
uint32_t tid = GetCurrentThreadId();
#else
- uint32_t tid = pthread_self();
+ pthread_t tid = pthread_self();
#endif
spall_buffer_end_ex(&ctx, &muh_buffer, nanos, tid, 0);
after this the project compiles but fails to link:
ninja: job failed: clang bin/objs/static.o bin/objs/main_driver.o bin/objs/common.o bin/objs/perf.o bin/objs/libcuik.o bin/objs/msvc.o bin/objs/gnu.o bin/objs/darwin.o bin/objs/libtb.o bin/objs/x64.o bin/objs/freestanding.o -g -lc -lm -g -o bin/cuik
/usr/bin/ld: bin/objs/libtb.o: in function `tb_pass_codegen':
/home/prokop/source/Cuik/tb/src/tb.c:81:(.text+0x7e9): undefined reference to `tb_platform_valloc'
/usr/bin/ld: /home/prokop/source/Cuik/tb/src/tb.c:183:(.text+0x821): undefined reference to `tb_platform_valloc'
/usr/bin/ld: bin/objs/libtb.o: in function `tb_module_destroy':
/home/prokop/source/Cuik/tb/src/tb.c:263:(.text+0xa7a): undefined reference to `tb_platform_vfree'
/usr/bin/ld: bin/objs/libtb.o: in function `tb_free_thread_resources':
/home/prokop/source/Cuik/tb/src/tb.c:529:(.text+0x19a5): undefined reference to `tb_platform_vfree'
/usr/bin/ld: bin/objs/libtb.o: in function `tb_tls_allocate':
/home/prokop/source/Cuik/tb/src/tb.c:536:(.text+0x19e5): undefined reference to `tb_platform_valloc'
/usr/bin/ld: bin/objs/libtb.o: in function `tb_tls_steal':
/home/prokop/source/Cuik/tb/src/tb.c:549:(.text+0x1a35): undefined reference to `tb_platform_valloc'
/usr/bin/ld: bin/objs/libtb.o: in function `tb_coff_write_output':
/home/prokop/source/Cuik/tb/src/tb.c:536:(.text+0xaca6): undefined reference to `tb_platform_valloc'
/usr/bin/ld: bin/objs/libtb.o: in function `tb_jit_begin':
/home/prokop/source/Cuik/tb/src/jit.c:379:(.text+0x10357): undefined reference to `tb_platform_valloc'
/usr/bin/ld: /home/prokop/source/Cuik/tb/src/jit.c:386:(.text+0x1038b): undefined reference to `tb_platform_vprotect'
/usr/bin/ld: bin/objs/libtb.o: in function `tb_jit_end':
/home/prokop/source/Cuik/tb/src/jit.c:392:(.text+0x103b5): undefined reference to `tb_platform_vfree'
/usr/bin/ld: bin/objs/libtb.o: in function `tb_pass_mem2reg':
/home/prokop/source/Cuik/tb/src/tb.c:549:(.text+0x1349e): undefined reference to `tb_platform_valloc'
/usr/bin/ld: bin/objs/libtb.o: in function `tb_pass_sroa':
/home/prokop/source/Cuik/tb/src/tb.c:549:(.text+0x163a1): undefined reference to `tb_platform_valloc'
/usr/bin/ld: bin/objs/libtb.o: in function `tb_linker_create':
/home/prokop/source/Cuik/tb/src/linker/linker.c:66:(.text+0x1cc26): undefined reference to `tb_platform_valloc'
/usr/bin/ld: bin/objs/x64.o: in function `tb_cgemit_reserve':
/home/prokop/source/Cuik/tb/src/x64/../emitter.h:115:(.text+0x4ea6): undefined reference to `tb_platform_valloc'
/usr/bin/ld: /home/prokop/source/Cuik/tb/src/x64/../emitter.h:115:(.text+0x4f43): undefined reference to `tb_platform_valloc'
/usr/bin/ld: bin/objs/x64.o:/home/prokop/source/Cuik/tb/src/x64/../emitter.h:115: more undefined references to `tb_platform_valloc' follow
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: subcommand failed
At this point i gave up and I'm looking for help
Thanks :D
Something is wrong in the optimizer.
identify_int_binop
has bad vibes.
It claims a * b = b
when b is zero... but it is coded like a * b = a
when b is zero.
should this:
case TB_SHL:
case TB_SHR:
case TB_ADD:
case TB_SUB:
case TB_MUL:
case TB_XOR:
return n->inputs[1];
be:
case TB_SHL:
case TB_SHR:
case TB_ADD:
case TB_SUB:
case TB_XOR:
return n->inputs[1];
case TB_MUL:
return n->inputs[2];
Or am i loosing my mind.
Hi, I believe I've discovered a bug in the Linux target, specifically, there seems to be an issue with how function calls with more than 5 parameters behave. I've attached a minimal reproducible example below:
#include "tb/include/tb.h"
#define CUIK_USE_TB
int main() {
TB_FeatureSet features = { 0 };
TB_Module* module = tb_module_create(TB_ARCH_X86_64, TB_SYSTEM_LINUX, &features, false);
TB_ModuleSectionHandle text = tb_module_get_text(module);
TB_PrototypeParam printf_ret = { TB_TYPE_I32 };
TB_PrototypeParam printf_param = { TB_TYPE_PTR };
// printf
TB_External* printf_external = tb_extern_create(module, 7, "printf", TB_EXTERNAL_SO_LOCAL);
TB_FunctionPrototype* printf_proto = tb_prototype_create(module, TB_STDCALL, 1, &printf_param, 1, &printf_ret, true);
// main
TB_PrototypeParam main_ret = { TB_TYPE_I32 };
// main
TB_Function* main_f = tb_function_create(module, 5, "main", TB_LINKAGE_PUBLIC);
TB_FunctionPrototype* main_prototype = tb_prototype_create(module, TB_STDCALL, 0, NULL, 1, &main_ret, false);
tb_function_set_prototype(main_f, text, main_prototype, NULL);
TB_Node* params[8] = {
tb_inst_string(main_f, 22, "%d %d %d %d %d %d %d\n"),
tb_inst_sint(main_f, TB_TYPE_I32, 1),
tb_inst_sint(main_f, TB_TYPE_I32, 2),
tb_inst_sint(main_f, TB_TYPE_I32, 3),
tb_inst_sint(main_f, TB_TYPE_I32, 4),
tb_inst_sint(main_f, TB_TYPE_I32, 5),
tb_inst_sint(main_f, TB_TYPE_I32, 6),
tb_inst_sint(main_f, TB_TYPE_I32, 7)
};
tb_inst_call(main_f, printf_proto, tb_inst_get_symbol_address(main_f, (TB_Symbol*)printf_external), 8, params);
TB_Node* ret_value = tb_inst_sint(main_f, TB_TYPE_I32, 0);
tb_inst_ret(main_f, 1, &ret_value);
TB_Passes* p_main = tb_pass_enter(main_f, tb_function_get_arena(main_f));
tb_pass_exit(p_main);
TB_ExportBuffer buffer = tb_module_object_export(module, TB_DEBUGFMT_NONE);
// copy into file
if (!tb_export_buffer_to_file(buffer, "./a.o")) {
printf("err\n");
}
return 0;
}
When we link the generated object file for TB_SYSTEM_LINUX
using clang like so:
$ clang a.o -o test
The program partially prints gibberish:
1 2 3 4 5 1776 1872767257
If we were to compile the same IR with TB_SYSTEM_WINDOWS
, and with the same command, we'd get the following, correct result:
1 2 3 4 5 6 7
Just curious - what are the goals, plans, and ideas behind this cool-looking repo?
I will add to this as I go along when I see things which may be missing.
const char*
also take a length where possible
ptrdiff_t
is -1, it's NUL terminatedf16
type
u16
internally or whateverTB_Node*
(useful in some instructions that require an explicit alignment)alloca
equivalenttb_inst_ptr
is missingtb_inst_sint
/tb_inst_uint
for types >64 bits (e.g. 128-bits)tb_inst_memmove
TB_
(SHL
|SHR
|SAR
) with slightly different to C semantics
x<<2
== (x<<1)<<1
TB_BSWAP
work with floats
TB_MULPAIR
TB_ArithmeticBehavior
maybe?wasm.memory.grow
wasm.memory.size
wasm.memory.atomic.wait32
wasm.memory.atomic.notify
tb_inst_memzero
helper?
TB_API void tb_inst_memzero(TB_Function* f, TB_Node* dst, TB_Node* val, TB_Node* count, TB_CharUnits align, bool is_volatile) {
tb_inst_memset(f, dst, tb_inst_uint(f, TB_TYPE_I8, 0), count, align, is_volatile);
}
cpu_relax
pause
on amd64isb
on arm64x86_cpuid
x86_xgetbv
tb_inst_set_region_name
require that the name passed is in unique to that function or does Tilde handle this?Rounding Ops would be great.
It would make up for the lack of tb_inst_fmod
a % b => a - trunc(a / b) * b
^^^^^ ^^^^^
this or this
Either of these are needed to do a fast fmod.
Rounding has a native instruction on x86 and many other platforms.
When TB_FEATURE_FRAME_PTR
is a feature changes alignment of stack, it just pushes without changing stack space used.
main:
push rbp
mov rbp, rsp
sub rsp, 88
vs
main:
sub rsp, 88
one of these is wrong. It would be good to get right.
I'm trying to create a simple function which returns a number in TB, but I'm having trouble doing so. This is my attempt:
#include "tb/include/tb.h"
#define CUIK_USE_TB
int main() {
TB_FeatureSet features = { 0 };
TB_Module* module = tb_module_create_for_host(&features, false);
TB_Function* function = tb_function_create(module, 4, "add", TB_LINKAGE_PRIVATE, TB_COMDAT_NONE);
TB_Node* number = tb_inst_sint(function, TB_TYPE_I64, 200);
tb_inst_ret(function, 0, NULL);
TB_Passes* p = tb_pass_enter(function, tb_function_get_arena(function));
tb_pass_codegen(p, true);
return 0;
}
When I run the above code I'd expect it to compile the function and print the respective assembly (the print_asm flag is set to true), but I crash as soon as I get to the tb_inst_sint
call. I'm most definitely doing something obvious incorrectly, but I'm not sure what, since when I went to check out the documentation the builder section was blank, which is perfectly understandable, thanks to the stage of the project. Any help related to getting this running is greatly appreciated!
The help message for cuik lists "help" as one of the valid commands, however when I try to run cuik it says "help" is an unkown command ("I have never seen this man before")
$ cuik help
Unknown command: help
As a side not i'd like for a way to look up information about various flags and options available in the compiler
Hi, say I wanted to declare a function like this one:
i32 func() {
return 200;
}
Would doing something like this suffice?
auto func = tb_function_create(m, 4, "func", somelinkage, etc);
auto constant = tb_inst_sint(func, TB_TYPE_I64, 200);
tb_inst_ret(func, 1, &constant );
Thanks for any help, I've tried looking through the manuals but I was unable to find anything, probably because it wouldn't make much sense to develop a full doc when the IR format is still under development which may change how things work.
When I try to compile my project with cuik I get an error on the following line:
return make_bool(isinf(f1));
Note the isinf function, which is a standard library function. The error says:
error: C:/programs/blisp/code/wrap.c:261:0: Could not parse expression!
261| _Pragma ( "warning ( suppress : 6334 )" )
| ^~~~~~~
Test case:
struct Expr typedef Expr;
struct Expr {
Expr *car;
Expr *cdr;
};
#define car(expr) expr->car
#define cdr(expr) expr->cdr
void x(Expr *args)
{
car(cdr(cdr(args)));
}
MSVC and Cland expand macro, as expected, to expr->cdr->cdr->car
. Cuik however prints the following error message:
$ cuik build code\test.c
error: code\test.c:13:0: could not resolve symbol: cdr
13| cdr(args) -> cdr
| ^~~
exited with 1 error
The file tb/.gitmodules
seems to do nothing.
There is a LuaJIT dependency in it.
Hi, I was wondering if you're ever planning to add support for different addressing modes for registers (RAX - EAX ...), and if so, how high/low it is on your to-do list.
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.
20 errors generated.
20 errors generated.
20 errors generated.
lld-link: error: build\arena.obj: machine type x86 conflicts with x64
lld-link: error: build\atoms.obj: machine type x86 conflicts with x64
lld-link: error: build\big_array.obj: machine type x86 conflicts with x64
lld-link: error: build\crash_handler.obj: machine type x86 conflicts with x64
lld-link: error: build\diagnostic.obj: machine type x86 conflicts with x64
lld-link: error: build\lexer.obj: machine type x86 conflicts with x64
lld-link: error: build\linker.obj: machine type x86 conflicts with x64
lld-link: error: build\microsoft_craziness.obj: machine type x86 conflicts with x64
lld-link: error: build\tblink.obj: machine type x86 conflicts with x64
lld-link: error: build\threads_msvc.obj: machine type x86 conflicts with x64
lld-link: error: build\tls.obj: machine type x86 conflicts with x64
lld-link: error: could not open 'ole32.lib': no such file or directory
lld-link: error: could not open 'Advapi32.lib': no such file or directory
lld-link: error: could not open 'OleAut32.lib': no such file or directory
lld-link: error: could not open 'DbgHelp.lib': no such file or directory
lld-link: error: could not open 'libcmt.lib': no such file or directory
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.