test: group examples into per-category folders
Move examples/*.sx and their expected/ snapshots into per-category subfolders (examples/<category>/...). Folder = leading filename token, with ffi-objc/ffi-jni kept whole; filenames are unchanged. The corpus runner and LSP sweep now discover each category's expected/ dir, while issues/ stays flat. Example 1058's repo-root-relative companion import is made file-relative. Path strings embedded in 164 snapshots were regenerated (path-only changes). Test-layout docs in CLAUDE.md updated.
This commit is contained in:
19
examples/platform/1600-platform-graphics.sx
Normal file
19
examples/platform/1600-platform-graphics.sx
Normal file
@@ -0,0 +1,19 @@
|
||||
#import "modules/ffi/raylib.sx";
|
||||
|
||||
main :: () {
|
||||
InitWindow(800, 600, "sx - Triangle");
|
||||
|
||||
while !WindowShouldClose() {
|
||||
BeginDrawing();
|
||||
ClearBackground(Color.{50, 50, 50, 255});
|
||||
|
||||
v1 := Vector2.{400.0, 100.0};
|
||||
v2 := Vector2.{200.0, 500.0};
|
||||
v3 := Vector2.{600.0, 500.0};
|
||||
DrawTriangle(v1, v2, v3, Color.{230, 41, 55, 255});
|
||||
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
CloseWindow();
|
||||
}
|
||||
280
examples/platform/1601-platform-sdl-graphics.sx
Normal file
280
examples/platform/1601-platform-sdl-graphics.sx
Normal file
@@ -0,0 +1,280 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/ffi/sdl3.sx";
|
||||
#import "modules/ffi/opengl.sx";
|
||||
#import "modules/math";
|
||||
|
||||
|
||||
WIDTH :f32: 800;
|
||||
HEIGHT :f32: 600;
|
||||
|
||||
vec4 :: (x: f32, y: f32, z: f32, w: f32) -> Vector(4, f32) {
|
||||
.[x, y, z, w]
|
||||
}
|
||||
|
||||
// ---- Matrix44: column-major 4×4 matrix ----
|
||||
|
||||
Matrix44 :: union {
|
||||
data: [16]f32;
|
||||
struct { c0, c1, c2, c3: Vector(4, f32); };
|
||||
}
|
||||
|
||||
mat4_multiply :: (a: *Matrix44, b: *Matrix44) -> Matrix44 {
|
||||
out : Matrix44 = ---;
|
||||
out.c0 = a.c0 * b.c0.x + a.c1 * b.c0.y + a.c2 * b.c0.z + a.c3 * b.c0.w;
|
||||
out.c1 = a.c0 * b.c1.x + a.c1 * b.c1.y + a.c2 * b.c1.z + a.c3 * b.c1.w;
|
||||
out.c2 = a.c0 * b.c2.x + a.c1 * b.c2.y + a.c2 * b.c2.z + a.c3 * b.c2.w;
|
||||
out.c3 = a.c0 * b.c3.x + a.c1 * b.c3.y + a.c2 * b.c3.z + a.c3 * b.c3.w;
|
||||
out
|
||||
}
|
||||
multiply :: ufcs mat4_multiply;
|
||||
|
||||
mat4_perspective :: (fov: f32, aspect: f32, near: f32, far: f32) -> Matrix44 {
|
||||
half := fov / 2.0;
|
||||
f := cos(half) / sin(half);
|
||||
m : Matrix44 = ---;
|
||||
m.c0 = vec4(f / aspect, 0.0, 0.0, 0.0);
|
||||
m.c1 = vec4(0.0, f, 0.0, 0.0);
|
||||
m.c2 = vec4(0.0, 0.0, (far + near) / (near - far), -1.0);
|
||||
m.c3 = vec4(0.0, 0.0, (2.0 * far * near) / (near - far), 0.0);
|
||||
m
|
||||
}
|
||||
|
||||
mat4_rotate_y :: (angle: f32) -> Matrix44 {
|
||||
c := cos(angle);
|
||||
s := sin(angle);
|
||||
m : Matrix44 = ---;
|
||||
m.c0 = vec4(c, 0.0, 0.0 - s, 0.0);
|
||||
m.c1 = vec4(0.0, 1.0, 0.0, 0.0);
|
||||
m.c2 = vec4(s, 0.0, c, 0.0);
|
||||
m.c3 = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
m
|
||||
}
|
||||
|
||||
mat4_rotate_x :: (angle: f32) -> Matrix44 {
|
||||
c := cos(angle);
|
||||
s := sin(angle);
|
||||
m : Matrix44 = ---;
|
||||
m.c0 = vec4(1.0, 0.0, 0.0, 0.0);
|
||||
m.c1 = vec4(0.0, c, s, 0.0);
|
||||
m.c2 = vec4(0.0, 0.0 - s, c, 0.0);
|
||||
m.c3 = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
m
|
||||
}
|
||||
|
||||
mat4_translate :: (tx: f32, ty: f32, tz: f32) -> Matrix44 {
|
||||
m : Matrix44 = ---;
|
||||
m.c0 = vec4(1.0, 0.0, 0.0, 0.0);
|
||||
m.c1 = vec4(0.0, 1.0, 0.0, 0.0);
|
||||
m.c2 = vec4(0.0, 0.0, 1.0, 0.0);
|
||||
m.c3 = vec4(tx, ty, tz, 1.0);
|
||||
m
|
||||
}
|
||||
|
||||
// ---- Main ----
|
||||
|
||||
main :: () {
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
|
||||
window := SDL_CreateWindow("sx GL cube", xx WIDTH, xx HEIGHT, SDL_WINDOW_OPENGL);
|
||||
gl_ctx := SDL_GL_CreateContext(window);
|
||||
SDL_GL_MakeCurrent(window, gl_ctx);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
load_gl(SDL_GL_GetProcAddress);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
print("create program: {}\n{}\n", VERT_SHADER_SRC, FRAG_SHADER_SRC);
|
||||
program := create_program(VERT_SHADER_SRC, FRAG_SHADER_SRC);
|
||||
glUseProgram(program);
|
||||
|
||||
mvp_loc : i32 = glGetUniformLocation(program, "uMVP");
|
||||
light_loc : i32 = glGetUniformLocation(program, "uLightDir");
|
||||
wire_loc : i32 = glGetUniformLocation(program, "uWire");
|
||||
|
||||
// Cube vertices: pos(vec4 w=1) + normal(vec4 w=0), 36 vertices × 2 vec4s = 72
|
||||
vertices : []Vector(4, f32) = .[
|
||||
// Front face (z = +0.5)
|
||||
.[-0.5, -0.5, 0.5, 1.0], vec4( 0.0, 0.0, 1.0, 0.0),
|
||||
vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||
vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||
vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||
vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||
vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||
// Back face (z = -0.5)
|
||||
vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||
vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||
vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||
vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||
vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||
vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||
// Top face (y = +0.5)
|
||||
vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||
vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||
vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||
vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||
vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||
vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||
// Bottom face (y = -0.5)
|
||||
vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||
vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||
vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||
vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||
vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||
vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||
// Right face (x = +0.5)
|
||||
vec4( 0.5, -0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||
vec4( 0.5, -0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||
vec4( 0.5, 0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||
vec4( 0.5, -0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||
vec4( 0.5, 0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||
vec4( 0.5, 0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||
// Left face (x = -0.5)
|
||||
vec4(-0.5, -0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0),
|
||||
vec4(-0.5, -0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0),
|
||||
vec4(-0.5, 0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0),
|
||||
vec4(-0.5, -0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0),
|
||||
vec4(-0.5, 0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0),
|
||||
vec4(-0.5, 0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0)
|
||||
];
|
||||
|
||||
// Create VAO and VBO
|
||||
vao : u32 = 0;
|
||||
vbo : u32 = 0;
|
||||
|
||||
glGenVertexArrays(1, vao);
|
||||
glGenBuffers(1, vbo);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, 1152, vertices.ptr, GL_STATIC_DRAW);
|
||||
|
||||
// Position attribute (location 0): 3 floats, stride 32 bytes, offset 0
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, 0, 32, xx 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
// Normal attribute (location 1): 3 floats, stride 32 bytes, offset 16
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, 0, 32, xx 16);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
// Set light direction
|
||||
glUniform3f(light_loc, 0.5, 0.7, 1.0);
|
||||
glUniform1f(wire_loc, 0.0);
|
||||
|
||||
// Render loop
|
||||
running := true;
|
||||
event : SDL_Event = .none;
|
||||
|
||||
while running {
|
||||
while SDL_PollEvent(event) {
|
||||
if event == {
|
||||
case .quit: running = false;
|
||||
case .key_up: (e) {
|
||||
if e.key == {
|
||||
case .escape: running = false;
|
||||
//case .
|
||||
}
|
||||
}
|
||||
case .window_exposed: (e) {
|
||||
|
||||
}
|
||||
case .key_down: (e) {
|
||||
k : u32 = xx e.key;
|
||||
print("ts={} wid={} sc={} key={}\n",
|
||||
e.timestamp, e.window_id, e.scancode, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute rotation angle from time
|
||||
ticks := SDL_GetTicks();
|
||||
ms : f32 = xx ticks;
|
||||
angle := ms * 0.001;
|
||||
|
||||
// Build matrices
|
||||
proj := mat4_perspective(PI / 4.0, WIDTH / HEIGHT, 0.1, 100.0);
|
||||
view := mat4_translate(0.0, 0.0, -3.0);
|
||||
rot_y := mat4_rotate_y(angle);
|
||||
rot_x := mat4_rotate_x(angle * 0.7);
|
||||
model := mat4_multiply(rot_y, rot_x);
|
||||
vm := mat4_multiply(view, model);
|
||||
mvp := mat4_multiply(proj, vm);
|
||||
|
||||
glUniformMatrix4fv(mvp_loc, 1, 0, mvp.data);
|
||||
|
||||
glClearColor(0.1, 0.1, 0.15, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Solid pass
|
||||
glUniform1f(wire_loc, 0.0);
|
||||
glBindVertexArray(vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
|
||||
// Wireframe overlay
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glLineWidth(2.0);
|
||||
glUniform1f(wire_loc, 1.0);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
|
||||
// Restore
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
glDepthFunc(GL_LESS);
|
||||
glUniform1f(wire_loc, 0.0);
|
||||
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
SDL_GL_DestroyContext(gl_ctx);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
VERT_SHADER_SRC : string = #string GLSL
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec3 aNormal;
|
||||
uniform mat4 uMVP;
|
||||
out vec3 vNormal;
|
||||
out vec3 vPos;
|
||||
|
||||
void main() {
|
||||
gl_Position = uMVP * vec4(aPos, 1.0);
|
||||
vNormal = aNormal;
|
||||
vPos = aPos;
|
||||
}
|
||||
GLSL;
|
||||
|
||||
FRAG_SHADER_SRC : string = #string GLSL
|
||||
#version 330 core
|
||||
in vec3 vNormal;
|
||||
in vec3 vPos;
|
||||
out vec4 FragColor;
|
||||
uniform vec3 uLightDir;
|
||||
uniform float uWire;
|
||||
void main() {
|
||||
if (uWire > 0.5) {
|
||||
FragColor = vec4(0.05, 0.05, 0.05, 1.0);
|
||||
return;
|
||||
}
|
||||
vec3 n = normalize(vNormal);
|
||||
vec3 l = normalize(uLightDir);
|
||||
float diff = max(dot(n, l), 0.15);
|
||||
float cx = floor(vPos.x * 2.0 + 0.001);
|
||||
float cy = floor(vPos.y * 2.0 + 0.001);
|
||||
float cz = floor(vPos.z * 2.0 + 0.001);
|
||||
float check = mod(cx + cy + cz, 2.0);
|
||||
vec3 col1 = vec3(0.9, 0.5, 0.2);
|
||||
vec3 col2 = vec3(0.2, 0.6, 0.9);
|
||||
vec3 base = mix(col1, col2, check);
|
||||
FragColor = vec4(base * diff, 1.0);
|
||||
}
|
||||
GLSL;
|
||||
81
examples/platform/1602-platform-http-server.sx
Normal file
81
examples/platform/1602-platform-http-server.sx
Normal file
@@ -0,0 +1,81 @@
|
||||
// HTTP server example (macOS only)
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/socket.sx";
|
||||
|
||||
// --- Logger ---
|
||||
|
||||
Logger :: struct {
|
||||
prefix: string;
|
||||
count: i64;
|
||||
}
|
||||
|
||||
log :: (logger: *Logger, msg: string) {
|
||||
logger.count += 1;
|
||||
print("[{}] {}\n", logger.prefix, msg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
main :: () -> i32 {
|
||||
PORT :: 8080;
|
||||
|
||||
fd := socket(AF_INET, SOCK_STREAM, 0);
|
||||
if fd < 0 {
|
||||
print("error: socket()\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
opt : i32 = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, @opt, 4);
|
||||
|
||||
addr := SockAddr.{ sin_len = 16, sin_family = 2, sin_port = htons(PORT) };
|
||||
|
||||
if bind(fd, @addr, 16) < 0 {
|
||||
print("error: bind()\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if listen(fd, 10) < 0 {
|
||||
print("error: listen()\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
print("listening on http://localhost:{}\n", PORT);
|
||||
|
||||
arena := Arena.init(context.allocator, 65536);
|
||||
logger := Logger.{ prefix = "http", count = 0 };
|
||||
|
||||
while true {
|
||||
client := accept(fd, null, null);
|
||||
if client < 0 { continue; }
|
||||
|
||||
push Context.{ allocator = xx arena, data = xx @logger } {
|
||||
handle(client);
|
||||
}
|
||||
|
||||
arena.reset();
|
||||
close(client);
|
||||
}
|
||||
|
||||
arena.deinit();
|
||||
close(fd);
|
||||
0
|
||||
}
|
||||
|
||||
handle :: (client: i32) {
|
||||
// Read request
|
||||
buf : [4096]u8 = ---;
|
||||
read(client, buf, buf.len);
|
||||
|
||||
body :: "<html><body><h1>Hello from sx!</h1></body></html>";
|
||||
response :: format("HTTP/1.1 200 OK\r
|
||||
Content-Type: text/html\r
|
||||
Connection: close\r
|
||||
Content-Length: {}\r
|
||||
\r\n{}", body.len, body);
|
||||
|
||||
write(client, response, response.len);
|
||||
logger : *Logger = xx context.data;
|
||||
log(logger, format("served request #{}", logger.count + 1));
|
||||
}
|
||||
16
examples/platform/1603-platform-stb-image.sx
Normal file
16
examples/platform/1603-platform-stb-image.sx
Normal file
@@ -0,0 +1,16 @@
|
||||
#import "modules/std.sx";
|
||||
stb :: #import "vendors/stb_image/stb_image.sx";
|
||||
|
||||
main :: () -> i32 {
|
||||
w: i32 = 0;
|
||||
h: i32 = 0;
|
||||
ch: i32 = 0;
|
||||
img := stb.stbi_load("test.png", @w, @h, @ch, 4);
|
||||
if xx img != 0 {
|
||||
print("loaded {}x{} ({} channels)\n", w, h, ch);
|
||||
stb.stbi_image_free(xx img);
|
||||
} else {
|
||||
print("no image (expected in test)\n");
|
||||
}
|
||||
0
|
||||
}
|
||||
45
examples/platform/1604-platform-build-config.sx
Normal file
45
examples/platform/1604-platform-build-config.sx
Normal file
@@ -0,0 +1,45 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/build.sx";
|
||||
|
||||
// --- #run build configuration ---
|
||||
// build_options() returns a BuildOptions struct at compile time.
|
||||
// Methods on it (add_link_flag, set_output_path) are compiler builtins
|
||||
// that configure the build without runtime cost.
|
||||
|
||||
configure_build :: () abi(.compiler) {
|
||||
opts := build_options();
|
||||
// These calls are intercepted by the compiler at compile time.
|
||||
// On a normal (non-wasm) target, inline if gates them off.
|
||||
inline if OS == .wasm {
|
||||
opts.set_output_path("sx-out/wasm/test.html");
|
||||
opts.add_link_flag("-sUSE_SDL=3");
|
||||
}
|
||||
}
|
||||
#run configure_build();
|
||||
|
||||
// --- inline if with compiler constants ---
|
||||
|
||||
main :: () {
|
||||
// Verify #run configure_build() executed without error
|
||||
print("build config: ok\n");
|
||||
|
||||
// Verify compiler constants are available
|
||||
print("pointer size: {}\n", POINTER_SIZE);
|
||||
|
||||
// Verify inline if with OS/ARCH works
|
||||
inline if OS == {
|
||||
case .macos: { print("os: macos\n"); }
|
||||
case .linux: { print("os: linux\n"); }
|
||||
case .windows: { print("os: windows\n"); }
|
||||
case .wasm: { print("os: wasm\n"); }
|
||||
else: { print("os: unknown\n"); }
|
||||
}
|
||||
|
||||
// Verify POINTER_SIZE is usable in inline if
|
||||
inline if POINTER_SIZE == 8 {
|
||||
print("64-bit platform\n");
|
||||
}
|
||||
inline if POINTER_SIZE == 4 {
|
||||
print("32-bit platform\n");
|
||||
}
|
||||
}
|
||||
12
examples/platform/1605-platform-frameworks.sx
Normal file
12
examples/platform/1605-platform-frameworks.sx
Normal file
@@ -0,0 +1,12 @@
|
||||
// `#framework "Name"` top-level directive registers an Apple framework for
|
||||
// linking; `extern` declarations can omit the library identifier (frameworks
|
||||
// resolve symbols by global namespace at link time).
|
||||
|
||||
#framework "CoreFoundation";
|
||||
|
||||
CFAbsoluteTimeGetCurrent :: () -> f64 extern;
|
||||
|
||||
main :: () -> i32 {
|
||||
t := CFAbsoluteTimeGetCurrent();
|
||||
return if t > 0.0 then 0 else 1;
|
||||
}
|
||||
121
examples/platform/1606-platform-metal-clear.sx
Normal file
121
examples/platform/1606-platform-metal-clear.sx
Normal file
@@ -0,0 +1,121 @@
|
||||
// iOS-only: bring up UIKitPlatform in Metal mode, clear the screen dark
|
||||
// blue each frame, then draw a colored triangle via the GPU protocol —
|
||||
// exercises create_shader (MSL compile + pipeline state), create_buffer
|
||||
// + update_buffer, set_shader, set_vertex_buffer, and draw_triangles.
|
||||
// Step 3b will port the UI renderer to use this same surface.
|
||||
//
|
||||
// Build for iOS sim:
|
||||
// /Users/agra/projects/sx/zig-out/bin/sx build --target ios-sim \
|
||||
// examples/63-metal-clear.sx \
|
||||
// -o /tmp/MetalClear --bundle /tmp/MetalClear.app \
|
||||
// --bundle-id co.swipelab.metalclear -F ~/Library/Frameworks
|
||||
// codesign --force --sign - --timestamp=none /tmp/MetalClear.app
|
||||
// xcrun simctl install booted /tmp/MetalClear.app
|
||||
// xcrun simctl launch --terminate-running-process booted co.swipelab.metalclear
|
||||
//
|
||||
// This file is iOS-only and not part of the JIT regression suite (no
|
||||
// tests/expected/63-metal-clear.txt). The test runner skips it on macOS.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/ffi/objc.sx";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/platform/api.sx";
|
||||
#import "modules/platform/uikit.sx";
|
||||
#import "modules/gpu/types.sx";
|
||||
#import "modules/gpu/api.sx";
|
||||
#import "modules/gpu/metal.sx";
|
||||
|
||||
#framework "UIKit";
|
||||
#framework "QuartzCore";
|
||||
#framework "OpenGLES";
|
||||
|
||||
// Pass-through vertex + fragment shader for a colored triangle. Vertex
|
||||
// layout is { packed_float2 pos; packed_float4 color; } = 24 bytes —
|
||||
// `packed_*` types have 4-byte alignment so the struct doesn't get
|
||||
// padded between fields (a plain `float4` would force 16-byte alignment
|
||||
// and pad the struct out to 32 bytes per vertex). Entry-point names
|
||||
// (vmain / fmain) match what MetalGPU.create_shader looks up.
|
||||
TRI_MSL :: #string MSL
|
||||
#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
struct Vertex {
|
||||
packed_float2 pos;
|
||||
packed_float4 color;
|
||||
};
|
||||
|
||||
struct RasterizerData {
|
||||
float4 position [[position]];
|
||||
float4 color;
|
||||
};
|
||||
|
||||
vertex RasterizerData vmain(uint vid [[vertex_id]],
|
||||
constant Vertex* vertices [[buffer(0)]]) {
|
||||
RasterizerData out;
|
||||
out.position = float4(vertices[vid].pos, 0.0, 1.0);
|
||||
out.color = float4(vertices[vid].color);
|
||||
return out;
|
||||
}
|
||||
|
||||
fragment float4 fmain(RasterizerData in [[stage_in]]) {
|
||||
return in.color;
|
||||
}
|
||||
MSL;
|
||||
|
||||
TRI_VERTS : [18]f32 = .[
|
||||
-0.6, -0.4, 1.0, 0.0, 0.0, 1.0,
|
||||
0.6, -0.4, 0.0, 1.0, 0.0, 1.0,
|
||||
0.0, 0.6, 0.0, 0.0, 1.0, 1.0,
|
||||
];
|
||||
|
||||
g_plat : *UIKitPlatform = null;
|
||||
g_gpu : *MetalGPU = null;
|
||||
g_shader : ShaderHandle = 0;
|
||||
g_vbuf : BufferHandle = 0;
|
||||
|
||||
frame :: () {
|
||||
if g_plat == null { return; }
|
||||
if g_gpu == null { return; }
|
||||
|
||||
// Lazy-init the GPU on the first frame where the layer is available
|
||||
// (the layer is created during -[SxAppDelegate didFinishLaunching:]
|
||||
// which fires AFTER our main() returns into UIApplicationMain).
|
||||
if g_gpu.layer == null {
|
||||
if g_plat.gl_layer == null { return; }
|
||||
if !g_gpu.init(g_plat.gl_layer, g_plat.pixel_w, g_plat.pixel_h) { return; }
|
||||
}
|
||||
|
||||
// Compile shader + upload vertex buffer once.
|
||||
if g_shader == 0 {
|
||||
g_shader = g_gpu.create_shader(TRI_MSL, "");
|
||||
if g_shader == 0 { return; }
|
||||
}
|
||||
if g_vbuf == 0 {
|
||||
g_vbuf = g_gpu.create_buffer(72); // 3 verts × 24 bytes
|
||||
if g_vbuf == 0 { return; }
|
||||
g_gpu.update_buffer(g_vbuf, xx @TRI_VERTS, 72);
|
||||
}
|
||||
|
||||
bg : ClearColor = .{ r = 0.07, g = 0.10, b = 0.18, a = 1.0 };
|
||||
if !g_gpu.begin_frame(bg) { return; }
|
||||
g_gpu.set_shader(g_shader);
|
||||
g_gpu.set_vertex_buffer(g_vbuf);
|
||||
g_gpu.draw_triangles(0, 3);
|
||||
g_gpu.end_frame(0.0);
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
inline if OS != .ios { return 0; }
|
||||
|
||||
plat : *UIKitPlatform = xx context.allocator.alloc_bytes(size_of(UIKitPlatform));
|
||||
plat.gpu_mode = .metal;
|
||||
if !plat.init("Metal Clear", 0, 0) { return 1; }
|
||||
g_plat = plat;
|
||||
|
||||
gpu : *MetalGPU = xx context.allocator.alloc_bytes(size_of(MetalGPU));
|
||||
g_gpu = gpu;
|
||||
|
||||
plat.run_frame_loop(closure(frame));
|
||||
plat.shutdown();
|
||||
0
|
||||
}
|
||||
45
examples/platform/1607-platform-uikit-app.sx
Normal file
45
examples/platform/1607-platform-uikit-app.sx
Normal file
@@ -0,0 +1,45 @@
|
||||
// Minimal iOS app entry point — pure sx, no .m files.
|
||||
//
|
||||
// 1. Register a class `SxAppDelegate : UIResponder <UIApplicationDelegate>`
|
||||
// dynamically, with one method:
|
||||
// application:didFinishLaunchingWithOptions: returns YES (BOOL 1).
|
||||
// 2. Call UIApplicationMain(0, null, null, @"SxAppDelegate") to hand off to
|
||||
// UIKit's run loop. This blocks until the app exits.
|
||||
//
|
||||
// After install + launch, the simulator shows the default black screen
|
||||
// (UIWindow not created — that's 5.8) and the AppDelegate callback fires
|
||||
// once at startup. The process stays alive because UIApplicationMain
|
||||
// drives the iOS run loop.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/ffi/objc.sx";
|
||||
|
||||
#framework "UIKit";
|
||||
|
||||
UIApplicationMain :: (argc: i32, argv: *void, principal_class: *NSString, delegate_class: *NSString) -> i32 extern;
|
||||
|
||||
// IMP for application:didFinishLaunchingWithOptions:
|
||||
// Obj-C: -(BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)opts
|
||||
// Type encoding: "c@:@@" -- BOOL (signed char), self, _cmd, id, id
|
||||
did_finish_launching :: (self: *void, _cmd: *void, app: *void, opts: *void) -> u8 abi(.c) {
|
||||
NSLog(xx "[sx] application:didFinishLaunchingWithOptions: called\n");
|
||||
return 1; // YES
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
// SxAppDelegate : UIResponder. We deliberately don't try
|
||||
// `class_addProtocol(UIApplicationDelegate)` — the linker dead-strips the
|
||||
// protocol metadata from UIKit when nothing references it at compile
|
||||
// time, and the C runtime can't look it up by name. UIApplicationMain
|
||||
// duck-types on the method name, so this works without formal conformance.
|
||||
UIResponder := objc_getClass("UIResponder".ptr);
|
||||
SxAppDelegate := objc_allocateClassPair(UIResponder, "SxAppDelegate".ptr, 0);
|
||||
|
||||
sel := sel_registerName("application:didFinishLaunchingWithOptions:".ptr);
|
||||
class_addMethod(SxAppDelegate, sel, xx did_finish_launching, "c@:@@".ptr);
|
||||
|
||||
objc_registerClassPair(SxAppDelegate);
|
||||
|
||||
// Hand off to the iOS run loop. Never returns under normal operation.
|
||||
return UIApplicationMain(0, xx 0, xx 0, xx "SxAppDelegate");
|
||||
}
|
||||
98
examples/platform/1608-platform-uikit-window.sx
Normal file
98
examples/platform/1608-platform-uikit-window.sx
Normal file
@@ -0,0 +1,98 @@
|
||||
// Show something on screen — a UIWindow with a colored UIViewController root.
|
||||
//
|
||||
// Builds on 5.7's AppDelegate. Inside `didFinishLaunching`, we:
|
||||
// 1. [[UIWindow alloc] initWithFrame:CGRect]
|
||||
// 2. [[UIViewController alloc] init]; set view.backgroundColor
|
||||
// 3. window.rootViewController = vc
|
||||
// 4. [window makeKeyAndVisible]
|
||||
//
|
||||
// We hardcode the frame to 1024×1024 to avoid the struct-return ABI of
|
||||
// `[UIScreen mainScreen].bounds` for now — any frame bigger than the device
|
||||
// covers the screen, modulo safe-area insets.
|
||||
//
|
||||
// `g_window` is a module-level global so the window outlives the IMP scope
|
||||
// (no ARC; UIKit retains it as the key window anyway).
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/ffi/objc.sx";
|
||||
|
||||
#framework "UIKit";
|
||||
|
||||
UIApplicationMain :: (argc: i32, argv: *void, principal_class: *NSString, delegate_class: *NSString) -> i32 extern;
|
||||
|
||||
g_window : *void = ---;
|
||||
|
||||
// AppDelegate's `window` property. iOS queries this getter to discover the
|
||||
// app's key window; without it, the legacy code path creates its own empty
|
||||
// window and ignores anything we configure.
|
||||
window_getter :: (self: *void, _cmd: *void) -> *void abi(.c) {
|
||||
return g_window;
|
||||
}
|
||||
window_setter :: (self: *void, _cmd: *void, w: *void) abi(.c) {
|
||||
g_window = w;
|
||||
}
|
||||
|
||||
did_finish_launching :: (self: *void, _cmd: *void, app: *void, opts: *void) -> u8 abi(.c) {
|
||||
UIWindow := objc_getClass("UIWindow".ptr);
|
||||
UIViewController := objc_getClass("UIViewController".ptr);
|
||||
UIColor := objc_getClass("UIColor".ptr);
|
||||
|
||||
sel_alloc := sel_registerName("alloc".ptr);
|
||||
sel_init := sel_registerName("init".ptr);
|
||||
sel_init_with_scene := sel_registerName("initWithWindowScene:".ptr);
|
||||
sel_view := sel_registerName("view".ptr);
|
||||
sel_system_blue := sel_registerName("systemBlueColor".ptr);
|
||||
sel_set_bg := sel_registerName("setBackgroundColor:".ptr);
|
||||
sel_set_root_vc := sel_registerName("setRootViewController:".ptr);
|
||||
sel_make_key_visible := sel_registerName("makeKeyAndVisible".ptr);
|
||||
sel_connected_scenes := sel_registerName("connectedScenes".ptr);
|
||||
sel_any_object := sel_registerName("anyObject".ptr);
|
||||
|
||||
msg_o : (*void, *void) -> *void abi(.c) = xx objc_msgSend;
|
||||
msg_v : (*void, *void) -> void abi(.c) = xx objc_msgSend;
|
||||
msg_oo : (*void, *void, *void) -> void abi(.c) = xx objc_msgSend;
|
||||
msg_ooo : (*void, *void, *void) -> *void abi(.c) = xx objc_msgSend;
|
||||
|
||||
// Modern iOS path: get the connected windowScene, then construct the
|
||||
// window via initWithWindowScene: so UIKit auto-sizes it and attaches
|
||||
// it to the display in one step.
|
||||
scenes := msg_o(app, sel_connected_scenes);
|
||||
scene := msg_o(scenes, sel_any_object);
|
||||
if scene == xx 0 { NSLog(xx "[sx] scene NULL\n"); return 1; }
|
||||
NSLog(xx "[sx] scene: ok\n");
|
||||
|
||||
win_raw := msg_o(UIWindow, sel_alloc);
|
||||
g_window = msg_ooo(win_raw, sel_init_with_scene, scene);
|
||||
NSLog(xx "[sx] window: ok\n");
|
||||
|
||||
vc_raw := msg_o(UIViewController, sel_alloc);
|
||||
vc := msg_o(vc_raw, sel_init);
|
||||
msg_oo(g_window, sel_set_root_vc, vc);
|
||||
|
||||
blue := msg_o(UIColor, sel_system_blue);
|
||||
view := msg_o(vc, sel_view);
|
||||
msg_oo(view, sel_set_bg, blue);
|
||||
msg_oo(g_window, sel_set_bg, blue);
|
||||
|
||||
msg_v(g_window, sel_make_key_visible);
|
||||
NSLog(xx "[sx] makeKeyAndVisible done\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
UIResponder := objc_getClass("UIResponder".ptr);
|
||||
SxAppDelegate := objc_allocateClassPair(UIResponder, "SxAppDelegate".ptr, 0);
|
||||
|
||||
class_addMethod(SxAppDelegate,
|
||||
sel_registerName("application:didFinishLaunchingWithOptions:".ptr),
|
||||
xx did_finish_launching, "c@:@@".ptr);
|
||||
class_addMethod(SxAppDelegate,
|
||||
sel_registerName("window".ptr),
|
||||
xx window_getter, "@@:".ptr);
|
||||
class_addMethod(SxAppDelegate,
|
||||
sel_registerName("setWindow:".ptr),
|
||||
xx window_setter, "v@:@".ptr);
|
||||
|
||||
objc_registerClassPair(SxAppDelegate);
|
||||
return UIApplicationMain(0, xx 0, xx 0, xx "SxAppDelegate");
|
||||
}
|
||||
20
examples/platform/1609-platform-add-framework.sx
Normal file
20
examples/platform/1609-platform-add-framework.sx
Normal file
@@ -0,0 +1,20 @@
|
||||
// BuildOptions.add_framework registers an Apple framework at comptime,
|
||||
// equivalent to a top-level `#framework "Name"` directive. The advantage:
|
||||
// you can gate it with `inline if OS == .ios { ... }` or similar logic,
|
||||
// keeping the framework off non-Apple builds.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/build.sx";
|
||||
|
||||
configure_build :: () abi(.compiler) {
|
||||
opts := build_options();
|
||||
opts.add_framework("CoreFoundation");
|
||||
}
|
||||
#run configure_build();
|
||||
|
||||
CFAbsoluteTimeGetCurrent :: () -> f64 extern;
|
||||
|
||||
main :: () -> i32 {
|
||||
t := CFAbsoluteTimeGetCurrent();
|
||||
return if t > 0.0 then 0 else 1;
|
||||
}
|
||||
96
examples/platform/1610-platform-uikit-platform.sx
Normal file
96
examples/platform/1610-platform-uikit-platform.sx
Normal file
@@ -0,0 +1,96 @@
|
||||
// UIKitPlatform end-to-end smoke: boots the AppDelegate, installs an
|
||||
// SxGLView with a CAEAGLLayer + GLES3 context + CADisplayLink, polls
|
||||
// UITouch events into ui.Event, and on every vsync clears the screen
|
||||
// to a color that advances on each tap. Each tap also toggles the
|
||||
// on-screen keyboard so safe_insets.bottom can be observed growing /
|
||||
// shrinking under it.
|
||||
//
|
||||
// To visualize the safe-area / keyboard inset, the frame draws a red
|
||||
// bar at the bottom whose height equals `safe_insets.bottom`. The
|
||||
// platform interpolates `keyboard_height` over the keyboard's own
|
||||
// animation duration, so the bar slides in lockstep with iOS's
|
||||
// keyboard.
|
||||
//
|
||||
// Build + run:
|
||||
// sx build --target ios-sim examples/66-uikit-platform.sx \
|
||||
// -o /tmp/SxUIKitBoot --bundle /tmp/SxUIKitBoot.app \
|
||||
// --bundle-id co.swipelab.sxuikit -F ~/Library/Frameworks
|
||||
// xcrun simctl install booted /tmp/SxUIKitBoot.app
|
||||
// xcrun simctl launch booted co.swipelab.sxuikit
|
||||
|
||||
#import "modules/std.sx";
|
||||
#framework "UIKit";
|
||||
#framework "OpenGLES";
|
||||
#framework "QuartzCore";
|
||||
#import "modules/ffi/opengl.sx";
|
||||
#import "modules/ui/types.sx";
|
||||
#import "modules/ui/events.sx";
|
||||
#import "modules/platform/uikit.sx";
|
||||
|
||||
GL_SCISSOR_TEST :u32: 0x0C11;
|
||||
glEnable_ : (u32) -> void = ---;
|
||||
glDisable_ : (u32) -> void = ---;
|
||||
glScissor_ : (i32, i32, i32, i32) -> void = ---;
|
||||
|
||||
g_color_index : i64 = 0;
|
||||
g_keyboard_up : bool = false;
|
||||
g_loaded : bool = false;
|
||||
|
||||
tap_frame :: () {
|
||||
fc := g_uikit_plat.begin_frame();
|
||||
|
||||
if !g_loaded {
|
||||
// Cache the GL fn-ptrs we use beyond what modules/ffi/opengl.sx loads.
|
||||
glEnable_ = xx ios_gl_proc("glEnable".ptr);
|
||||
glDisable_ = xx ios_gl_proc("glDisable".ptr);
|
||||
glScissor_ = xx ios_gl_proc("glScissor".ptr);
|
||||
g_loaded = true;
|
||||
}
|
||||
|
||||
events := g_uikit_plat.poll_events();
|
||||
i : i64 = 0;
|
||||
while i < events.len {
|
||||
ev := events.ptr[i];
|
||||
if ev == {
|
||||
case .mouse_down: {
|
||||
g_color_index += 1;
|
||||
if g_keyboard_up {
|
||||
g_uikit_plat.hide_keyboard();
|
||||
g_keyboard_up = false;
|
||||
} else {
|
||||
g_uikit_plat.show_keyboard();
|
||||
g_keyboard_up = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
phase := g_color_index % 3;
|
||||
r : f32 = if phase == 0 then 0.8 else 0.1;
|
||||
g : f32 = if phase == 1 then 0.8 else 0.1;
|
||||
b : f32 = if phase == 2 then 0.8 else 0.1;
|
||||
|
||||
glViewport(0, 0, fc.pixel_w, fc.pixel_h);
|
||||
glClearColor(r, g, b, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Bottom bar = the interpolated safe-area bottom inset.
|
||||
insets := g_uikit_plat.safe_insets();
|
||||
bar_h_px : i32 = xx (insets.bottom * fc.dpi_scale);
|
||||
if bar_h_px > 0 {
|
||||
glEnable_(GL_SCISSOR_TEST);
|
||||
glScissor_(0, 0, fc.pixel_w, bar_h_px);
|
||||
glClearColor(0.95, 0.25, 0.25, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDisable_(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
g_uikit_plat.end_frame();
|
||||
}
|
||||
|
||||
main :: () -> void {
|
||||
plat : *UIKitPlatform = xx libc_malloc(size_of(UIKitPlatform));
|
||||
plat.init("SxUIKitPlatform", 0, 0);
|
||||
plat.run_frame_loop(closure(tap_frame));
|
||||
}
|
||||
26
examples/platform/1611-platform-post-link-callback.sx
Normal file
26
examples/platform/1611-platform-post-link-callback.sx
Normal file
@@ -0,0 +1,26 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/build.sx";
|
||||
|
||||
// Post-link callback registration. The compiler invokes `post_link`
|
||||
// after `target.link()` returns (sx build). Under `sx run` (JIT) the
|
||||
// callback is registered but never invoked because there's no link
|
||||
// phase — so the only thing this example prints under the test
|
||||
// runner is `runtime main`. The post-link path is exercised via
|
||||
// `sx build` separately.
|
||||
|
||||
puts :: (s: [:0]u8) -> i32 extern libc;
|
||||
|
||||
post_link :: (opt: BuildOptions) -> bool abi(.compiler) {
|
||||
puts("[post-link] callback fired");
|
||||
true
|
||||
}
|
||||
|
||||
configure :: () abi(.compiler) {
|
||||
opts := build_options();
|
||||
on_build(post_link);
|
||||
}
|
||||
#run configure();
|
||||
|
||||
main :: () {
|
||||
print("runtime main\n");
|
||||
}
|
||||
43
examples/platform/1612-platform-fs-roundtrip.sx
Normal file
43
examples/platform/1612-platform-fs-roundtrip.sx
Normal file
@@ -0,0 +1,43 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/fs.sx";
|
||||
|
||||
// fs.sx smoke test: every primitive the bundling phase needs.
|
||||
// Creates a temp tree, writes/reads/copies/renames/chmod/deletes
|
||||
// through it, then exercises basename/dirname.
|
||||
|
||||
main :: () {
|
||||
if !create_dir_all("/tmp/sx_fs_test/a/b/c") { print("FAIL mkdir_all\n"); return; }
|
||||
if !exists("/tmp/sx_fs_test/a/b/c") { print("FAIL exists after mkdir_all\n"); return; }
|
||||
|
||||
if !write_file("/tmp/sx_fs_test/hello.txt", "hello fs") { print("FAIL write_file\n"); return; }
|
||||
if r := read_file("/tmp/sx_fs_test/hello.txt") {
|
||||
if r.len != 8 { print("FAIL read length: got {}\n", r.len); return; }
|
||||
print("read: {}\n", r);
|
||||
} else {
|
||||
print("FAIL read_file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if !copy_file("/tmp/sx_fs_test/hello.txt", "/tmp/sx_fs_test/hello.copy") { print("FAIL copy\n"); return; }
|
||||
if !exists("/tmp/sx_fs_test/hello.copy") { print("FAIL exists after copy\n"); return; }
|
||||
|
||||
if !move("/tmp/sx_fs_test/hello.copy", "/tmp/sx_fs_test/renamed.txt") { print("FAIL rename\n"); return; }
|
||||
if exists("/tmp/sx_fs_test/hello.copy") { print("FAIL old still exists after rename\n"); return; }
|
||||
if !exists("/tmp/sx_fs_test/renamed.txt") { print("FAIL new missing after rename\n"); return; }
|
||||
|
||||
if !set_mode("/tmp/sx_fs_test/hello.txt", 493) { print("FAIL chmod\n"); return; }
|
||||
|
||||
if !delete_file("/tmp/sx_fs_test/hello.txt") { print("FAIL delete_file\n"); return; }
|
||||
if !delete_file("/tmp/sx_fs_test/renamed.txt") { print("FAIL delete renamed\n"); return; }
|
||||
if !delete_dir("/tmp/sx_fs_test/a/b/c") { print("FAIL delete c\n"); return; }
|
||||
if !delete_dir("/tmp/sx_fs_test/a/b") { print("FAIL delete b\n"); return; }
|
||||
if !delete_dir("/tmp/sx_fs_test/a") { print("FAIL delete a\n"); return; }
|
||||
if !delete_dir("/tmp/sx_fs_test") { print("FAIL delete root\n"); return; }
|
||||
|
||||
print("basename(/a/b/c.txt) = {}\n", basename("/a/b/c.txt"));
|
||||
print("basename(foo) = {}\n", basename("foo"));
|
||||
print("dirname(/a/b/c.txt) = {}\n", dirname("/a/b/c.txt"));
|
||||
print("dirname(foo) = {}\n", dirname("foo"));
|
||||
|
||||
print("ok\n");
|
||||
}
|
||||
47
examples/platform/1613-platform-process-roundtrip.sx
Normal file
47
examples/platform/1613-platform-process-roundtrip.sx
Normal file
@@ -0,0 +1,47 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/process.sx";
|
||||
|
||||
// process.sx smoke test: run + env + find_executable, with
|
||||
// success-path and failure-path coverage.
|
||||
//
|
||||
// The PATH-startswith check is stable across machines (PATH always
|
||||
// begins with an absolute path); `ls` is guaranteed in /bin on every
|
||||
// POSIX host this targets.
|
||||
|
||||
main :: () {
|
||||
if r := run("echo hello world") {
|
||||
print("exit={}, stdout={}", r.exit_code, r.stdout);
|
||||
} else {
|
||||
print("FAIL run echo\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if r := run("false") {
|
||||
if r.exit_code == 0 { print("FAIL: false should not exit 0\n"); return; }
|
||||
print("false exit={}\n", r.exit_code);
|
||||
}
|
||||
|
||||
if n := env("SX_DEFINITELY_UNSET_VAR") {
|
||||
print("FAIL: unset var returned: {}\n", n);
|
||||
return;
|
||||
}
|
||||
print("unset var: null (ok)\n");
|
||||
|
||||
if w := find_executable("ls") {
|
||||
// /bin/ls on macOS, /usr/bin/ls on Linux. Either is fine —
|
||||
// we only assert the result is non-empty and absolute.
|
||||
if w.len < 2 { print("FAIL: ls path too short\n"); return; }
|
||||
if w[0] != 47 { print("FAIL: ls path not absolute\n"); return; }
|
||||
print("ls is absolute (ok)\n");
|
||||
} else {
|
||||
print("FAIL find ls\n"); return;
|
||||
}
|
||||
|
||||
if w := find_executable("sx_definitely_no_such_command_12345") {
|
||||
print("FAIL: bogus exec returned: {}\n", w);
|
||||
return;
|
||||
}
|
||||
print("missing exec: null (ok)\n");
|
||||
|
||||
print("ok\n");
|
||||
}
|
||||
18
examples/platform/1614-platform-macos-bundle.sx
Normal file
18
examples/platform/1614-platform-macos-bundle.sx
Normal file
@@ -0,0 +1,18 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/platform/bundle.sx";
|
||||
|
||||
// Register the sx-side `.app` bundler. Under `sx build` on macOS, the
|
||||
// post-link callback runs and writes a real `.app` next to the
|
||||
// binary. Under `sx run` (JIT) the callback is registered but never
|
||||
// fires — so the test runner only sees `runtime main`.
|
||||
|
||||
configure :: () abi(.compiler) {
|
||||
opts := build_options();
|
||||
opts.set_bundle_path("HelloApp.app");
|
||||
opts.set_bundle_id("co.example.hello");
|
||||
on_build(bundle_main);
|
||||
}
|
||||
#run configure();
|
||||
|
||||
main :: () { print("runtime main\n"); }
|
||||
20
examples/platform/1615-platform-ios-sim-bundle.sx
Normal file
20
examples/platform/1615-platform-ios-sim-bundle.sx
Normal file
@@ -0,0 +1,20 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/platform/bundle.sx";
|
||||
|
||||
// Cross-compile regression for the iOS-simulator branch of
|
||||
// `platform.bundle`. On a host with the iPhoneSimulator SDK installed,
|
||||
// `sx build --target ios-sim` writes a `.app` with the iOS-shaped
|
||||
// Info.plist (UIDeviceFamily, LSRequiresIPhoneOS,
|
||||
// UIApplicationSceneManifest, DTPlatformName=iPhoneSimulator). Ad-hoc
|
||||
// codesign; no provisioning embed needed for the simulator.
|
||||
|
||||
configure :: () abi(.compiler) {
|
||||
opts := build_options();
|
||||
opts.set_bundle_path("IosSimApp.app");
|
||||
opts.set_bundle_id("co.example.iossim");
|
||||
on_build(bundle_main);
|
||||
}
|
||||
#run configure();
|
||||
|
||||
main :: () { print("ios-sim runtime main\n"); }
|
||||
58
examples/platform/1616-platform-ios-device-bundle.sx
Normal file
58
examples/platform/1616-platform-ios-device-bundle.sx
Normal file
@@ -0,0 +1,58 @@
|
||||
// iOS *device* end-to-end exercise for `platform.bundle`. Distinct from
|
||||
// 121-ios-sim-bundle.sx because the device path adds three steps that
|
||||
// don't run on the simulator: provisioning profile embed, entitlements
|
||||
// extraction (`security cms` + `plutil` pipeline resolving the
|
||||
// wildcard `<TEAM>.*` → `<TEAM>.<bundle_id>`), and codesign with
|
||||
// `--entitlements`.
|
||||
//
|
||||
// Build:
|
||||
// sx build --target ios examples/122-ios-device-bundle.sx -o /tmp/SxDeviceProbe
|
||||
//
|
||||
// Install + launch (requires the device UDID to be on the profile):
|
||||
// xcrun devicectl device install app --device <name> /tmp/SxDeviceProbe.app
|
||||
// xcrun devicectl device process launch --device <name> co.swipelab.sxprobe
|
||||
//
|
||||
// The bundle id (`co.swipelab.sxprobe`) and codesign identity below
|
||||
// match the test team's wildcard `SwipeS32DevProfile.mobileprovision`.
|
||||
// Update the three set_* values to your own identity / profile / id to
|
||||
// re-exercise on a different developer account.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/ffi/objc.sx";
|
||||
|
||||
#framework "UIKit";
|
||||
|
||||
UIApplicationMain :: (argc: i32, argv: *void, principal_class: *NSString, delegate_class: *NSString) -> i32 extern;
|
||||
#import "modules/build.sx";
|
||||
#import "modules/platform/bundle.sx";
|
||||
|
||||
configure :: () abi(.compiler) {
|
||||
opts := build_options();
|
||||
opts.set_bundle_path("/tmp/SxDeviceProbe.app");
|
||||
opts.set_bundle_id("co.swipelab.sxprobe");
|
||||
opts.set_codesign_identity("Apple Development: Alexandru Agrapine (DC8VVHJ9W4)");
|
||||
opts.set_provisioning_profile("/Users/agra/Downloads/SwipeS32DevProfile.mobileprovision");
|
||||
on_build(bundle_main);
|
||||
}
|
||||
#run configure();
|
||||
|
||||
// IMP for application:didFinishLaunchingWithOptions:
|
||||
// Obj-C: -(BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)opts
|
||||
// Type encoding: "c@:@@" -- BOOL (signed char), self, _cmd, id, id
|
||||
did_finish_launching :: (self: *void, _cmd: *void, app: *void, opts: *void) -> u8 abi(.c) {
|
||||
NSLog(xx "[sx-device-probe] launched\n");
|
||||
return 1; // YES
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
UIResponder := objc_getClass("UIResponder".ptr);
|
||||
SxAppDelegate := objc_allocateClassPair(UIResponder, "SxAppDelegate".ptr, 0);
|
||||
|
||||
sel := sel_registerName("application:didFinishLaunchingWithOptions:".ptr);
|
||||
class_addMethod(SxAppDelegate, sel, xx did_finish_launching, "c@:@@".ptr);
|
||||
|
||||
objc_registerClassPair(SxAppDelegate);
|
||||
|
||||
// UIApplicationMain blocks driving iOS's run loop.
|
||||
return UIApplicationMain(0, xx 0, xx 0, xx "SxAppDelegate");
|
||||
}
|
||||
9
examples/platform/1638-platform-target-host.sx
Normal file
9
examples/platform/1638-platform-target-host.sx
Normal file
@@ -0,0 +1,9 @@
|
||||
// Phase 0 (ASM stream) test-infra lock: exercises the `<name>.build` JSON
|
||||
// config + `--target` threading + the host-match EXECUTE path of the corpus
|
||||
// runner. The companion `.build` pins the HOST target (`{ "target": "macos" }`
|
||||
// resolves to the host arch+os), so the runner threads `--target` and still
|
||||
// runs the example natively — its stdout is asserted as usual.
|
||||
#import "modules/std.sx";
|
||||
main :: () {
|
||||
print("target-host ok\n");
|
||||
}
|
||||
7
examples/platform/1639-platform-target-cross.sx
Normal file
7
examples/platform/1639-platform-target-cross.sx
Normal file
@@ -0,0 +1,7 @@
|
||||
// Phase 0 (ASM stream) test-infra lock: exercises the corpus runner's
|
||||
// CROSS-TARGET ir-only path. The `.build` pins `x86_64-linux`, which does NOT
|
||||
// match this aarch64 host, so the runner skips run/build/exec and verifies via
|
||||
// `sx ir --target x86_64-linux` only — asserting exit + the `.ir` snapshot +
|
||||
// stderr (no `.stdout`). Asm-free on purpose: it locks the harness gating, not
|
||||
// any inline-asm lowering (that arrives in Phase A+).
|
||||
main :: () -> i64 { return 0; }
|
||||
20
examples/platform/1640-platform-asm-parse.sx
Normal file
20
examples/platform/1640-platform-asm-parse.sx
Normal file
@@ -0,0 +1,20 @@
|
||||
// ASM stream Phase E — x86_64 multi-output asm: `divq` produces quotient in rax
|
||||
// and remainder in rdx, returned as a `(quot, rem)` tuple. Two `={rax}`/`={rdx}`
|
||||
// value outputs ⇒ LLVM returns a `{ i64, i64 }` struct, which IS sx's tuple
|
||||
// representation (so `q, r := …` destructures it directly). x86-pinned via
|
||||
// `.build`: ir-only on a non-x86 host (the `.ir` snapshot locks the struct
|
||||
// return + `%[name]` rewrite); runs natively on x86_64-linux. See 1647 for a
|
||||
// multi-output example that executes on aarch64.
|
||||
divmod :: (n: u64, d: u64) -> (quot: u64, rem: u64) {
|
||||
return asm {
|
||||
"divq %[d]",
|
||||
[quot] "={rax}" -> u64,
|
||||
[rem] "={rdx}" -> u64,
|
||||
"{rax}" = n, "{rdx}" = 0, [d] "r" = d,
|
||||
clobbers(.cc),
|
||||
};
|
||||
}
|
||||
|
||||
main :: () {
|
||||
q, r := divmod(17, 5);
|
||||
}
|
||||
6
examples/platform/1641-platform-asm-missing-volatile.sx
Normal file
6
examples/platform/1641-platform-asm-missing-volatile.sx
Normal file
@@ -0,0 +1,6 @@
|
||||
// ASM stream Phase B — an asm with no value outputs yields no result, so its
|
||||
// effects could be deleted unless it is marked `volatile`. This omits
|
||||
// `volatile` ⇒ a compile error. Pins that diagnostic (mirrors Zig's rule).
|
||||
// Called from `main` so lowering reaches the asm body.
|
||||
nope :: () { asm { "nop" }; }
|
||||
main :: () { nope(); }
|
||||
5
examples/platform/1642-platform-asm-nop-volatile.sx
Normal file
5
examples/platform/1642-platform-asm-nop-volatile.sx
Normal file
@@ -0,0 +1,5 @@
|
||||
// ASM stream — the no-output `volatile` form runs end-to-end: a bare `nop`
|
||||
// (no operands, no result) assembles and executes cleanly (exit 0). Confirms
|
||||
// the no-output⇒volatile rule's positive side AND the zero-operand emit path.
|
||||
nop :: () { asm volatile { "nop" }; }
|
||||
main :: () { nop(); }
|
||||
6
examples/platform/1643-platform-asm-echo-name.sx
Normal file
6
examples/platform/1643-platform-asm-echo-name.sx
Normal file
@@ -0,0 +1,6 @@
|
||||
// ASM stream Phase B — operand naming rule (§II.5): an explicit `[name]` that
|
||||
// just echoes the register its own constraint pins (`[eax] "={eax}"`) carries
|
||||
// no information — the operand is already auto-named after the register. Reject
|
||||
// it. The useful form is a label that DIFFERS (e.g. `[quot] "={rax}"`).
|
||||
f :: () -> u32 { return asm volatile { "cpuid", [eax] "={eax}" -> u32, "{eax}" = 1 }; }
|
||||
main :: () { x := f(); }
|
||||
4
examples/platform/1644-platform-asm-duplicate-name.sx
Normal file
4
examples/platform/1644-platform-asm-duplicate-name.sx
Normal file
@@ -0,0 +1,4 @@
|
||||
// ASM stream Phase B — two asm operands may not share a `[name]`: the `%[name]`
|
||||
// template reference (and the result tuple field) would be ambiguous.
|
||||
f :: () -> u64 { return asm volatile { "nop", [x] "=r" -> u64, [x] "r" = 5 }; }
|
||||
main :: () { v := f(); }
|
||||
10
examples/platform/1645-platform-asm-aarch64-add.sx
Normal file
10
examples/platform/1645-platform-asm-aarch64-add.sx
Normal file
@@ -0,0 +1,10 @@
|
||||
// ASM stream Phase D — inline assembly that RUNS end-to-end. An aarch64 `add`
|
||||
// with two register-class inputs (`%[a]`, `%[b]`) and a value output (`%[out]`)
|
||||
// returned from the function. The `.build` pins aarch64-macOS: on a matching
|
||||
// host the runner executes it (exit 42); elsewhere it falls to ir-only mode and
|
||||
// asserts the `.ir` snapshot (the inline_asm op + LLVM `call asm` are target-
|
||||
// independent in the IR text). Regression for the full lower→emit→JIT path.
|
||||
add_asm :: (a: i64, b: i64) -> i64 {
|
||||
return asm { "add %[out], %[a], %[b]", [out] "=r" -> i64, [a] "r" = a, [b] "r" = b };
|
||||
}
|
||||
main :: () -> i64 { return add_asm(40, 2); }
|
||||
9
examples/platform/1646-platform-asm-value-binding.sx
Normal file
9
examples/platform/1646-platform-asm-value-binding.sx
Normal file
@@ -0,0 +1,9 @@
|
||||
// ASM stream Phase D — a bare `x := asm { … -> T }` binding (not a direct
|
||||
// `return asm`) types correctly: the value output flows through the local and
|
||||
// out as the exit code. Regression for the `inferType` `.asm_expr` arm (without
|
||||
// it the binding inferred `.unresolved` and silently produced 0). aarch64-pinned
|
||||
// via `.build` → runs on a matching host, ir-only elsewhere.
|
||||
main :: () -> i64 {
|
||||
x := asm { "mov %[out], #99", [out] "=r" -> i64 };
|
||||
return x;
|
||||
}
|
||||
20
examples/platform/1647-platform-asm-aarch64-multi.sx
Normal file
20
examples/platform/1647-platform-asm-aarch64-multi.sx
Normal file
@@ -0,0 +1,20 @@
|
||||
// ASM stream Phase E — multi-output asm that RUNS end-to-end on aarch64. Splits
|
||||
// a value into low/high bytes via two value outputs, returned + destructured as
|
||||
// a `(lo, hi)` tuple. The two outputs become an LLVM `{ i64, i64 }` struct =
|
||||
// sx's tuple. aarch64-pinned via `.build`: executes on a matching host (exit
|
||||
// reflects lo+hi), ir-only elsewhere.
|
||||
split :: (x: u64) -> (lo: u64, hi: u64) {
|
||||
return asm {
|
||||
#string ASM
|
||||
and %[l], %[x], #0xff
|
||||
lsr %[h], %[x], #8
|
||||
ASM,
|
||||
[l] "=r" -> u64,
|
||||
[h] "=r" -> u64,
|
||||
[x] "r" = x,
|
||||
};
|
||||
}
|
||||
main :: () -> i64 {
|
||||
lo, hi := split(0x1234);
|
||||
return xx (lo + hi); // 0x34 + 0x12 = 52 + 18 = 70
|
||||
}
|
||||
20
examples/platform/1648-platform-asm-global.sx
Normal file
20
examples/platform/1648-platform-asm-global.sx
Normal file
@@ -0,0 +1,20 @@
|
||||
// ASM stream Phase F — top-level (global) `asm { … }`: a template-only block at
|
||||
// module scope, lowered to LLVM `module asm` (LLVMAppendModuleInlineAsm). It
|
||||
// defines a symbol that a lib-less `extern` declaration calls into — the
|
||||
// import direction reuses the existing C-FFI extern path, no new surface.
|
||||
// Built+run via `aot` (a module-asm symbol lives in the final linked binary,
|
||||
// not the JIT host); aarch64-macos-pinned, so ir-only on a non-matching host.
|
||||
asm {
|
||||
#string ASM
|
||||
.global _my_add
|
||||
_my_add:
|
||||
add x0, x0, x1
|
||||
ret
|
||||
ASM,
|
||||
};
|
||||
|
||||
my_add :: (a: i64, b: i64) -> i64 extern;
|
||||
|
||||
main :: () -> i64 {
|
||||
return my_add(40, 2); // 42, computed by the global-asm routine
|
||||
}
|
||||
19
examples/platform/1649-platform-asm-place-output.sx
Normal file
19
examples/platform/1649-platform-asm-place-output.sx
Normal file
@@ -0,0 +1,19 @@
|
||||
// ASM stream Phase 2 — `-> @place` write-through output. An asm result can be
|
||||
// STORED through a place (a local / struct field) instead of returned: the
|
||||
// place output does NOT join the result tuple. Here one value output is
|
||||
// returned (into `main_val`) while a second is written through `@other`. The
|
||||
// two are combined to 42. Read-write (`+`) and indirect (`*`) place outputs are
|
||||
// not yet implemented (rejected at lowering). aarch64-pinned; ir-only elsewhere.
|
||||
compute :: () -> i64 {
|
||||
other : i64 = 0;
|
||||
main_val := asm volatile {
|
||||
#string ASM
|
||||
mov %[m], #5
|
||||
mov %[o], #37
|
||||
ASM,
|
||||
[m] "=r" -> i64, // value output → returned
|
||||
[o] "=r" -> @other, // place output → stored through @other
|
||||
};
|
||||
return main_val + other; // 5 + 37 = 42
|
||||
}
|
||||
main :: () -> i64 { return compute(); }
|
||||
11
examples/platform/1650-platform-asm-rw-place.sx
Normal file
11
examples/platform/1650-platform-asm-rw-place.sx
Normal file
@@ -0,0 +1,11 @@
|
||||
// ASM stream Phase 2 — read-write (`+`) place output. The place is LOADED as a
|
||||
// seed, the asm both reads and writes the operand register (tied input ↔ output),
|
||||
// and the (modified) result is STORED back through the place. Increment-in-place:
|
||||
// the register holds 41 on entry, the asm adds 1, 42 is written back to `x`.
|
||||
// aarch64-pinned; ir-only elsewhere.
|
||||
compute :: () -> i64 {
|
||||
x : i64 = 41;
|
||||
asm volatile { "add %[v], %[v], #1", [v] "+r" -> @x };
|
||||
return x; // 42
|
||||
}
|
||||
main :: () -> i64 { return compute(); }
|
||||
27
examples/platform/1651-platform-asm-x86-syscall-write.sx
Normal file
27
examples/platform/1651-platform-asm-x86-syscall-write.sx
Normal file
@@ -0,0 +1,27 @@
|
||||
// ASM stream — x86_64 Linux `write(2)` via a raw `syscall`. The canonical inline-
|
||||
// asm use case: SYS_write (rax=1) with fd/buf/count pinned to rdi/rsi/rdx, the
|
||||
// `syscall` instruction clobbering rcx + r11 (+ memory), and the byte count
|
||||
// returned in rax. Demonstrates register-pinned inputs, a pinned value output,
|
||||
// a pointer input (`*u8` → rsi), and `clobbers(.…)` lowering all at once.
|
||||
//
|
||||
// x86-pinned via `.build`: ir-only on a non-x86 host — the `.ir` snapshot locks
|
||||
// the exact constraint string (`={rax},{rax},{rdi},{rsi},{rdx},~{rcx},~{r11},
|
||||
// ~{memory}`), which is the §II.11 silent-miscompile risk zone — and runs
|
||||
// natively on x86_64-linux (printing "ok\n"). See 1640 for an x86 multi-output
|
||||
// example, 1645/1647/1649/1650 for aarch64 examples that execute on this host.
|
||||
sys_write :: (fd: i64, buf: *u8, count: i64) -> i64 {
|
||||
return asm volatile {
|
||||
"syscall",
|
||||
[ret] "={rax}" -> i64, // return: bytes written, in rax
|
||||
"{rax}" = 1, // SYS_write (x86_64 Linux)
|
||||
"{rdi}" = fd, // fd
|
||||
"{rsi}" = buf, // buf
|
||||
"{rdx}" = count, // count
|
||||
clobbers(.rcx, .r11, .memory),
|
||||
};
|
||||
}
|
||||
|
||||
main :: () {
|
||||
msg : [3]u8 = .[111, 107, 10]; // "ok\n"
|
||||
n := sys_write(1, @msg[0], 3);
|
||||
}
|
||||
19
examples/platform/1652-platform-asm-indirect-mem.sx
Normal file
19
examples/platform/1652-platform-asm-indirect-mem.sx
Normal file
@@ -0,0 +1,19 @@
|
||||
// ASM stream — indirect-memory (`=*m`) place output. The place address is passed
|
||||
// to the asm as a pointer and the asm writes THROUGH it (no return slot): here
|
||||
// `str x9, %[out]` stores 42 into `x`'s storage directly. Distinct from a
|
||||
// write-through `=` output (which returns a value that is then stored). Mixes a
|
||||
// value output and an input below to exercise operand ordering. aarch64-pinned;
|
||||
// ir-only elsewhere (the `.ir` locks the `=*m` constraint + `elementtype` attr).
|
||||
poke :: () -> i64 {
|
||||
x : i64 = 0;
|
||||
asm volatile {
|
||||
#string ASM
|
||||
mov x9, #42
|
||||
str x9, %[out]
|
||||
ASM,
|
||||
[out] "=*m" -> @x,
|
||||
clobbers(.x9),
|
||||
};
|
||||
return x; // 42 — written through the pointer
|
||||
}
|
||||
main :: () -> i64 { return poke(); }
|
||||
22
examples/platform/1653-platform-asm-global-jit.sx
Normal file
22
examples/platform/1653-platform-asm-global-jit.sx
Normal file
@@ -0,0 +1,22 @@
|
||||
// ASM stream — global (module-scope) `asm { … }` executed via the JIT (`sx run`),
|
||||
// NOT AOT. `sx run` compiles the whole module to an in-memory object (the
|
||||
// integrated assembler assembles the `module asm` block into it), then ORC
|
||||
// relocates and runs it — so a module-asm symbol IS resolvable at JIT main
|
||||
// execution, just like a normal symbol. The only path that can't see it is a
|
||||
// COMPILE-TIME `#run` call (the interpreter resolves externs via host dlsym; the
|
||||
// symbol isn't linked yet — see 1654). Sibling of 1648 (which exercises the same
|
||||
// feature via AOT). aarch64-macos-pinned; ir-only elsewhere.
|
||||
asm {
|
||||
#string ASM
|
||||
.global _my_sub
|
||||
_my_sub:
|
||||
sub x0, x0, x1
|
||||
ret
|
||||
ASM,
|
||||
};
|
||||
|
||||
my_sub :: (a: i64, b: i64) -> i64 extern;
|
||||
|
||||
main :: () -> i64 {
|
||||
return my_sub(44, 2); // 42, computed by the global-asm routine, under JIT
|
||||
}
|
||||
22
examples/platform/1654-platform-asm-global-comptime-call.sx
Normal file
22
examples/platform/1654-platform-asm-global-comptime-call.sx
Normal file
@@ -0,0 +1,22 @@
|
||||
// ASM stream — calling a global-asm symbol at COMPILE TIME (`#run`) fails loud.
|
||||
// A module-asm symbol only exists once the module is assembled+linked; the
|
||||
// comptime VM resolves `extern` calls via host `dlsym` (RTLD_DEFAULT),
|
||||
// where the symbol is absent — so `#run my_add(…)` cannot evaluate and reports a
|
||||
// clear diagnostic instead of silently misfiring. (Calling the same symbol at
|
||||
// RUNTIME works under both JIT and AOT — see 1648/1653.) The failure is at
|
||||
// dlsym resolution, before any asm is assembled, so it is arch-independent —
|
||||
// no `.build` target needed. Regression guard for the comptime boundary.
|
||||
asm {
|
||||
#string ASM
|
||||
.global _my_add
|
||||
_my_add:
|
||||
add x0, x0, x1
|
||||
ret
|
||||
ASM,
|
||||
};
|
||||
|
||||
my_add :: (a: i64, b: i64) -> i64 extern;
|
||||
|
||||
COMPUTED :: #run my_add(40, 2); // compile-time call — module-asm symbol not yet linked
|
||||
|
||||
main :: () -> i64 { return COMPUTED; }
|
||||
35
examples/platform/1655-platform-asm-callback-into-sx.sx
Normal file
35
examples/platform/1655-platform-asm-callback-into-sx.sx
Normal file
@@ -0,0 +1,35 @@
|
||||
// ASM stream — the round trip: sx → asm → sx. A global-asm routine (`_caller`)
|
||||
// CALLS BACK into an sx function (`cb`) by its symbol, then returns. For the asm
|
||||
// `bl _cb` to resolve, the sx callback needs EXTERNAL LINKAGE and a stable C
|
||||
// symbol — that is exactly what `export` provides (it also implies the C ABI, so
|
||||
// no hidden context parameter). `abi(.c)` alone is NOT enough: it sets the
|
||||
// ABI but leaves the function `internal`, so it is dead-code-eliminated (nothing
|
||||
// in the IR references it — the `bl` is opaque to the optimizer) and `_cb` is
|
||||
// undefined at link. macOS gives `export "cb"` the symbol `_cb` (leading
|
||||
// underscore), which the template references. aarch64-macos-pinned; runs under
|
||||
// the JIT here (sx run), ir-only elsewhere.
|
||||
|
||||
// The sx callback — `export` gives it external linkage + the `_cb` symbol + C ABI.
|
||||
cb :: (n: i64) -> i64 export "cb" {
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
// A global-asm trampoline that calls back into `cb`. It saves/restores the link
|
||||
// register (x30) around the `bl` — it was itself reached via `bl`, so the return
|
||||
// address must survive the nested call.
|
||||
asm {
|
||||
#string ASM
|
||||
.global _caller
|
||||
_caller:
|
||||
stp x29, x30, [sp, #-16]!
|
||||
bl _cb // x0 = cb(x0) — back into sx
|
||||
ldp x29, x30, [sp], #16
|
||||
ret
|
||||
ASM,
|
||||
};
|
||||
|
||||
caller :: (n: i64) -> i64 extern;
|
||||
|
||||
main :: () -> i64 {
|
||||
return caller(41); // sx main → asm caller → bl _cb → sx cb → 42
|
||||
}
|
||||
25
examples/platform/1656-platform-asm-symbol-operand.sx
Normal file
25
examples/platform/1656-platform-asm-symbol-operand.sx
Normal file
@@ -0,0 +1,25 @@
|
||||
// ASM stream — symbol operand (`"s"`): feed a function/global SYMBOL into the
|
||||
// template so a DIRECT `bl %[fn]` (PC-relative — one fewer indirection than a
|
||||
// register-indirect `blr`: no pointer load, a relative reloc, a predictable
|
||||
// branch) goes straight to it. The backend emits the platform-mangled name
|
||||
// (`_cb` on macOS, `cb` on Linux), so the template stays portable — no hardcoded
|
||||
// underscore. Round trip: sx → asm → `bl _cb` → sx → 42. aarch64-macos-pinned;
|
||||
// runs under the JIT here, ir-only elsewhere (the `.ir` locks `"s"`/`ptr @cb`).
|
||||
cb :: (n: i64) -> i64 export "cb" { return n + 1; }
|
||||
|
||||
tramp :: (n: i64) -> i64 {
|
||||
return asm volatile {
|
||||
#string ASM
|
||||
stp x29, x30, [sp, #-16]!
|
||||
mov x0, %[arg]
|
||||
bl %[fn]
|
||||
mov %[res], x0
|
||||
ldp x29, x30, [sp], #16
|
||||
ASM,
|
||||
[res] "=r" -> i64,
|
||||
[arg] "r" = n,
|
||||
[fn] "s" = cb, // symbol operand → direct `bl _cb`
|
||||
clobbers(.x0, .x30, .memory),
|
||||
};
|
||||
}
|
||||
main :: () -> i64 { return tramp(41); }
|
||||
12
examples/platform/1657-platform-asm-x86-rw-place.sx
Normal file
12
examples/platform/1657-platform-asm-x86-rw-place.sx
Normal file
@@ -0,0 +1,12 @@
|
||||
// ASM stream — read-write (`+`) place output on x86_64 (cross-arch sibling of
|
||||
// the aarch64 1650). `incq %[v]` reads the operand register, increments it, and
|
||||
// the result is stored back through the place. Locks the x86 lowering of `+`:
|
||||
// an output `=r` plus a tied input (`=r,0`) seeded with the place's value.
|
||||
// x86-pinned via `.build`: ir-only here (the `.ir` is the assertion), runs
|
||||
// natively on x86_64-linux (main returns 0 on success, 1 if the asm misbehaved).
|
||||
bump :: () -> i64 {
|
||||
x : i64 = 41;
|
||||
asm volatile { "incq %[v]", [v] "+r" -> @x };
|
||||
return x; // 42
|
||||
}
|
||||
main :: () -> i64 { if bump() != 42 { return 1; } return 0; }
|
||||
12
examples/platform/1658-platform-asm-x86-indirect-mem.sx
Normal file
12
examples/platform/1658-platform-asm-x86-indirect-mem.sx
Normal file
@@ -0,0 +1,12 @@
|
||||
// ASM stream — indirect-memory (`=*m`) place output on x86_64 (cross-arch sibling
|
||||
// of the aarch64 1652). `movq $42, %[out]` stores straight through the place's
|
||||
// address — the address is passed as an opaque `ptr` with an `elementtype(i64)`
|
||||
// attribute, no return slot. Note `$42`: a literal `$` in the template is escaped
|
||||
// to LLVM's `$$` and emitted back as `$42` (an x86 immediate). x86-pinned;
|
||||
// ir-only here, runs on x86_64-linux.
|
||||
poke :: () -> i64 {
|
||||
x : i64 = 0;
|
||||
asm volatile { "movq $42, %[out]", [out] "=*m" -> @x };
|
||||
return x; // 42
|
||||
}
|
||||
main :: () -> i64 { if poke() != 42 { return 1; } return 0; }
|
||||
19
examples/platform/1659-platform-asm-x86-symbol-operand.sx
Normal file
19
examples/platform/1659-platform-asm-x86-symbol-operand.sx
Normal file
@@ -0,0 +1,19 @@
|
||||
// ASM stream — symbol operand (`"s"`) on x86_64 (cross-arch sibling of the
|
||||
// aarch64 1656). A DIRECT `call` to an `export`ed sx function by symbol, written
|
||||
// with the SAME portable `%[fn]` as the aarch64 example — the compiler injects
|
||||
// the `:c` operand modifier for symbol operands, so the symbol prints bare on
|
||||
// every target (x86 would otherwise render `$cb`, a bad call target). The
|
||||
// backend emits the platform-mangled name (`call cb` on Linux). x86-pinned;
|
||||
// ir-only here, runs on x86_64-linux. Round trip: sx → asm → call cb → sx → 42.
|
||||
cb :: (n: i64) -> i64 export "cb" { return n + 1; }
|
||||
|
||||
tramp :: (n: i64) -> i64 {
|
||||
return asm volatile {
|
||||
"call %[fn]",
|
||||
[ret] "={rax}" -> i64,
|
||||
"{rdi}" = n, // arg in rdi (SysV)
|
||||
[fn] "s" = cb, // symbol operand → direct `call cb`
|
||||
clobbers(.rcx, .rdx, .rsi, .r8, .r9, .r10, .r11, .memory),
|
||||
};
|
||||
}
|
||||
main :: () -> i64 { if tramp(41) != 42 { return 1; } return 0; }
|
||||
29
examples/platform/1660-platform-windows-win32-print.sx
Normal file
29
examples/platform/1660-platform-windows-win32-print.sx
Normal file
@@ -0,0 +1,29 @@
|
||||
// Windows x86_64 — print "42" and exit(0) through the Win32 system-call
|
||||
// boundary. The Windows analog of the Linux raw-`syscall` write (see
|
||||
// 1651): Windows has no stable raw syscall ABI (NtWriteFile's ordinal
|
||||
// shifts between OS builds), so the documented boundary IS kernel32 —
|
||||
// `GetStdHandle` + `WriteFile` to print, `ExitProcess` to terminate.
|
||||
//
|
||||
// Exercises the bundled-`zig` link backend end to end: built with
|
||||
// `--target windows-gnu --self-contained`, zig cc (mingw) auto-resolves
|
||||
// kernel32, producing a PE32+ that prints "42\n" and exits 0.
|
||||
//
|
||||
// Pinned `x86_64-windows-gnu` via `.build`: ir-only on this non-Windows
|
||||
// host (the `.ir` snapshot locks the Win64-ABI lowering of the three
|
||||
// extern calls); runs end-to-end on a Windows x86_64 runner.
|
||||
|
||||
kernel32 :: #library "kernel32";
|
||||
|
||||
// DWORD = u32, HANDLE/LPVOID = *void, BOOL = i32.
|
||||
GetStdHandle :: (n_std_handle: u32) -> *void extern;
|
||||
WriteFile :: (file: *void, buf: *u8, n: u32, written: *u32, overlapped: *void) -> i32 extern;
|
||||
ExitProcess :: (code: u32) -> void extern;
|
||||
|
||||
main :: () {
|
||||
// STD_OUTPUT_HANDLE = (DWORD)-11 = 0xFFFFFFF5.
|
||||
out := GetStdHandle(0xFFFFFFF5);
|
||||
msg : [3]u8 = .[52, 50, 10]; // "42\n"
|
||||
written : u32 = 0;
|
||||
WriteFile(out, @msg[0], 3, @written, null);
|
||||
ExitProcess(0);
|
||||
}
|
||||
1
examples/platform/1662-platform-build-pipeline-queries.c
Normal file
1
examples/platform/1662-platform-build-pipeline-queries.c
Normal file
@@ -0,0 +1 @@
|
||||
long c_marker(void) { return 42; }
|
||||
32
examples/platform/1662-platform-build-pipeline-queries.sx
Normal file
32
examples/platform/1662-platform-build-pipeline-queries.sx
Normal file
@@ -0,0 +1,32 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/build.sx";
|
||||
#import "modules/compiler.sx";
|
||||
|
||||
// P5.4 — the build-pipeline query primitives on the comptime VM. A custom
|
||||
// `on_build` override inspects the build metadata (`c_object_paths` returns the
|
||||
// VM-built `List(string)` of C companion objects), then DELEGATES to the stdlib
|
||||
// `default_pipeline` for the real emit + link. The `#import c` source compiles to
|
||||
// one object, so `c_object_paths()` must be a one-element list of a non-empty
|
||||
// path. AOT runs the produced binary → "runtime main: 42".
|
||||
|
||||
#import c {
|
||||
#source "1662-platform-build-pipeline-queries.c";
|
||||
};
|
||||
|
||||
c_marker :: () -> i64 extern;
|
||||
|
||||
build :: (opt: BuildOptions) -> bool abi(.compiler) {
|
||||
objs := c_object_paths();
|
||||
if objs.len != 1 { return false; }
|
||||
if objs.items[0].len == 0 { return false; }
|
||||
// link_libraries must be a well-formed (possibly empty) list — touch each.
|
||||
libs := link_libraries();
|
||||
sum : i64 = 0;
|
||||
i : i64 = 0;
|
||||
while i < libs.len { sum += libs.items[i].len; i += 1; }
|
||||
if sum < 0 { return false; }
|
||||
return default_pipeline(opt); // the real emit + link
|
||||
}
|
||||
#run on_build(build);
|
||||
|
||||
main :: () { print("runtime main: {}\n", c_marker()); }
|
||||
21
examples/platform/1664-platform-on-build-callback.sx
Normal file
21
examples/platform/1664-platform-on-build-callback.sx
Normal file
@@ -0,0 +1,21 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/build.sx";
|
||||
|
||||
// P5.4 — the `on_build` override. A user `#run on_build(build);` replaces the
|
||||
// stdlib `default_pipeline` as the build driver; the compiler invokes it after
|
||||
// codegen with the `BuildOptions` handle. This build GROWS a `List` on the build
|
||||
// VM (comptime List growth works only on the VM — issue 0141; the legacy interp
|
||||
// fails it) then DELEGATES to `default_pipeline` for the real emit + link, so a
|
||||
// working binary is still produced → "runtime main".
|
||||
|
||||
build :: (opt: BuildOptions) -> bool abi(.compiler) {
|
||||
names := List(string).{};
|
||||
names.append("alpha");
|
||||
names.append("beta");
|
||||
names.append("gamma");
|
||||
if names.len != 3 { return false; }
|
||||
return default_pipeline(opt);
|
||||
}
|
||||
#run on_build(build);
|
||||
|
||||
main :: () { print("runtime main\n"); }
|
||||
21
examples/platform/1665-platform-macos-bundle-smoke.sx
Normal file
21
examples/platform/1665-platform-macos-bundle-smoke.sx
Normal file
@@ -0,0 +1,21 @@
|
||||
// macOS `.app` bundle smoke test — the corpus's first real bundler coverage.
|
||||
//
|
||||
// `default_pipeline` auto-bundles when `bundle_path` is set (build.sx imports the
|
||||
// sx bundler, so no explicit `on_build` is needed). `sx build` runs the bundler
|
||||
// after link, producing a signed `.app`. The `.build` `bundle` directive asserts
|
||||
// the `.app` structure (`Contents/MacOS`, `Info.plist`, `_CodeSignature`) and then
|
||||
// cleans it up. macOS-host ONLY — the directive skips the example on other hosts.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
configure :: () abi(.compiler) {
|
||||
opts := build_options();
|
||||
opts.set_bundle_path(".sx-tmp/1665-platform-macos-bundle-smoke.app");
|
||||
opts.set_bundle_id("co.example.bundlesmoke");
|
||||
}
|
||||
#run configure();
|
||||
|
||||
main :: () -> i32 {
|
||||
print("bundle smoke ok\n");
|
||||
return 0;
|
||||
}
|
||||
33
examples/platform/1666-platform-android-apk-smoke.sx
Normal file
33
examples/platform/1666-platform-android-apk-smoke.sx
Normal file
@@ -0,0 +1,33 @@
|
||||
// Android `.apk` bundle smoke test — the corpus's first Android bundler
|
||||
// coverage.
|
||||
//
|
||||
// `sx build --target android --apk <out.apk> --bundle-id <id> -o <lib.so>`
|
||||
// cross-compiles for aarch64-linux-android and runs the sx default_pipeline
|
||||
// → bundle_main, which drives javac/d8/aapt2/zipalign/apksigner to produce a
|
||||
// signed APK containing `AndroidManifest.xml`, `classes.dex`,
|
||||
// `lib/arm64-v8a/<lib.so>`, and `META-INF/` signatures. The `.build` `apk`
|
||||
// directive builds + inspects the zip entries, then cleans up.
|
||||
//
|
||||
// GATED on the Android SDK (auto-discovered at $ANDROID_HOME /
|
||||
// $ANDROID_SDK_ROOT / ~/Library/Android/sdk) + a real JDK on PATH — the macOS
|
||||
// `/usr/bin/javac` stub is not enough. When either is missing the example
|
||||
// SKIPS cleanly so a plain `zig build test` stays green.
|
||||
//
|
||||
// Build-only: `--target android` is a cross-compile, so the APK can't run on
|
||||
// the build host. Runtime launch is validated manually on an emulator/device.
|
||||
//
|
||||
// Shape mirrors 1424 (a `#jni_main` Activity whose `onCreate` calls
|
||||
// `super.onCreate(b)`, so the app is well-formed).
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/build.sx";
|
||||
|
||||
Bundle :: #jni_class("android/os/Bundle") extern { }
|
||||
|
||||
SxApp :: #jni_main #jni_class("co/swipelab/sxapksmoke/SxApp") {
|
||||
onCreate :: (self: *Self, b: *Bundle) {
|
||||
super.onCreate(b);
|
||||
}
|
||||
}
|
||||
|
||||
main :: () -> i32 { 0 }
|
||||
1
examples/platform/expected/1603-platform-stb-image.exit
Normal file
1
examples/platform/expected/1603-platform-stb-image.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
no image (expected in test)
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
--- build done ---
|
||||
build config: ok
|
||||
pointer size: 8
|
||||
os: macos
|
||||
64-bit platform
|
||||
1
examples/platform/expected/1605-platform-frameworks.exit
Normal file
1
examples/platform/expected/1605-platform-frameworks.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
--- build done ---
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
--- build done ---
|
||||
runtime main
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
read: hello fs
|
||||
basename(/a/b/c.txt) = c.txt
|
||||
basename(foo) = foo
|
||||
dirname(/a/b/c.txt) = /a/b
|
||||
dirname(foo) = .
|
||||
ok
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
exit=0, stdout=hello world
|
||||
false exit=1
|
||||
unset var: null (ok)
|
||||
ls is absolute (ok)
|
||||
missing exec: null (ok)
|
||||
ok
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
--- build done ---
|
||||
ios-sim runtime main
|
||||
@@ -0,0 +1 @@
|
||||
{ "target": "macos" }
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
target-host ok
|
||||
@@ -0,0 +1 @@
|
||||
{ "target": "x86_64-linux" }
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
6
examples/platform/expected/1639-platform-target-cross.ir
Normal file
6
examples/platform/expected/1639-platform-target-cross.ir
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define i32 @main() #0 {
|
||||
entry:
|
||||
ret i32 0
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
1
examples/platform/expected/1640-platform-asm-parse.build
Normal file
1
examples/platform/expected/1640-platform-asm-parse.build
Normal file
@@ -0,0 +1 @@
|
||||
{ "target": "x86_64-linux" }
|
||||
1
examples/platform/expected/1640-platform-asm-parse.exit
Normal file
1
examples/platform/expected/1640-platform-asm-parse.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
26
examples/platform/expected/1640-platform-asm-parse.ir
Normal file
26
examples/platform/expected/1640-platform-asm-parse.ir
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { i64, i64 } @divmod(i64 %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %0, ptr %alloca, align 8
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %1, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %alloca, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%asm = call { i64, i64 } asm "divq ${4}", "={rax},={rdx},{rax},{rdx},r,~{cc}"(i64 %load, i64 0, i64 %loadN)
|
||||
ret { i64, i64 } %asm
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define i32 @main() #0 {
|
||||
entry:
|
||||
%call = call { i64, i64 } @divmod(i64 17, i64 5)
|
||||
%tg = extractvalue { i64, i64 } %call, 0
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %tg, ptr %alloca, align 8
|
||||
%tgN = extractvalue { i64, i64 } %call, 1
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %tgN, ptr %allocaN, align 8
|
||||
ret i32 0
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
error: asm expression with no outputs must be marked `volatile`
|
||||
--> examples/platform/1641-platform-asm-missing-volatile.sx:5:14
|
||||
|
|
||||
5 | nope :: () { asm { "nop" }; }
|
||||
| ^^^^^^^^^^^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
error: redundant asm operand name `eax` — it already names the pinned register; drop the `[eax]`
|
||||
--> examples/platform/1643-platform-asm-echo-name.sx:5:25
|
||||
|
|
||||
5 | f :: () -> u32 { return asm volatile { "cpuid", [eax] "={eax}" -> u32, "{eax}" = 1 }; }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
error: duplicate asm operand name `x`
|
||||
--> examples/platform/1644-platform-asm-duplicate-name.sx:3:25
|
||||
|
|
||||
3 | f :: () -> u64 { return asm volatile { "nop", [x] "=r" -> u64, [x] "r" = 5 }; }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{ "target": "macos" }
|
||||
@@ -0,0 +1 @@
|
||||
42
|
||||
21
examples/platform/expected/1645-platform-asm-aarch64-add.ir
Normal file
21
examples/platform/expected/1645-platform-asm-aarch64-add.ir
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal i64 @add_asm(i64 %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %0, ptr %alloca, align 8
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %1, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %alloca, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%asm = call i64 asm "add ${0}, ${1}, ${2}", "=r,r,r"(i64 %load, i64 %loadN)
|
||||
ret i64 %asm
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define i32 @main() #0 {
|
||||
entry:
|
||||
%call = call i64 @add_asm(i64 40, i64 2)
|
||||
%ca.tr = trunc i64 %call to i32
|
||||
ret i32 %ca.tr
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{ "target": "macos" }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user