diff --git a/demo/benchmarker/build.sh b/demo/benchmarker/build.sh new file mode 100755 index 000000000..6e3384fb7 --- /dev/null +++ b/demo/benchmarker/build.sh @@ -0,0 +1 @@ +gluc main.glu diff --git a/demo/benchmarker/compute.swift b/demo/benchmarker/compute.swift new file mode 100644 index 000000000..0ed00a6a8 --- /dev/null +++ b/demo/benchmarker/compute.swift @@ -0,0 +1,40 @@ +import Foundation +import CryptoKit + +/// Compute SHA256 hash of data +public func hashBytes( + data: UnsafeRawPointer, + len: Int, + output: UnsafeMutableRawPointer +) { + let buffer = UnsafeRawBufferPointer(start: data, count: len) + let digest = SHA256.hash(data: buffer) + digest.withUnsafeBytes { bytes in + output.copyMemory(from: bytes.baseAddress!, byteCount: 32) + } +} + +/// Verify if data matches expected hash +public func verifyHash( + data: UnsafeRawPointer, + len: Int, + expected: UnsafeRawPointer +) -> Bool { + let buffer = UnsafeRawBufferPointer(start: data, count: len) + let digest = SHA256.hash(data: buffer) + + let expectedBuffer = UnsafeRawBufferPointer(start: expected, count: 32) + return digest.withUnsafeBytes { actual in + return actual.elementsEqual(expectedBuffer) + } +} + +/// Compare two hashes for equality +public func compareHashes( + hash1: UnsafeRawPointer, + hash2: UnsafeRawPointer +) -> Bool { + let buffer1 = UnsafeRawBufferPointer(start: hash1, count: 32) + let buffer2 = UnsafeRawBufferPointer(start: hash2, count: 32) + return buffer1.elementsEqual(buffer2) +} diff --git a/demo/benchmarker/compute.zig b/demo/benchmarker/compute.zig new file mode 100644 index 000000000..0ce18bc88 --- /dev/null +++ b/demo/benchmarker/compute.zig @@ -0,0 +1,19 @@ +const std = @import("std"); +const sha256 = std.crypto.hash.sha2.Sha256; + +/// Compute SHA256 hash of data +export fn hashBytes(data: [*]const u8, len: usize, output: *[32]u8) void { + sha256.hash(data[0..len], output, .{}); +} + +/// Verify if data matches expected hash +export fn verifyHash(data: [*]const u8, len: usize, expected: *const [32]u8) bool { + var actual: [32]u8 = undefined; + sha256.hash(data[0..len], &actual, .{}); + return std.mem.eql(u8, &actual, expected); +} + +/// Compare two hashes for equality +export fn compareHashes(hash1: *const [32]u8, hash2: *const [32]u8) bool { + return std.mem.eql(u8, hash1, hash2); +} diff --git a/demo/benchmarker/hexprint.rs b/demo/benchmarker/hexprint.rs new file mode 100644 index 000000000..69de43699 --- /dev/null +++ b/demo/benchmarker/hexprint.rs @@ -0,0 +1,17 @@ + +#![no_main] + +/// Print byte slice as hex to stdout +#[no_mangle] +pub fn print_hex(data: &[u8]) { + for byte in data { + print!("{:02x}", byte); + } + println!(); +} + +/// Print 32-byte hash array as hex to stdout +#[no_mangle] +pub fn print_hash(hash: &[u8; 32]) { + print_hex(hash); +} diff --git a/demo/benchmarker/main.glu b/demo/benchmarker/main.glu new file mode 100644 index 000000000..3e9100b8c --- /dev/null +++ b/demo/benchmarker/main.glu @@ -0,0 +1,40 @@ +import timer::Timer; + +@file_extension("zig") import compute as compute_zig; +@file_extension("swift") import compute::compute as compute_swift; + +import hexprint::hexprint; +import random::random; + +let debug_print: Bool = true; + +func doWork() { + var data: UInt8[100]; + let length: UInt64 = 100; + for i in 0..<1000000 { + random::fill_random(data, length); + var zig: UInt8[32] = {0}; + var swift: UInt8[32] = {0}; + compute_zig::hashBytes(data, length, &zig); + compute_swift::hashBytes({(&data) as *Char}, {length}, {&swift as *Char}); + std::assert(compute_zig::verifyHash(data, length, &swift)); + std::assert(compute_swift::verifyHash({(&data) as *Char}, {length}, {&zig as *Char})); + if debug_print { + std::printf("Iteration %d:\n", i); + std::print("Swift hash:"); + hexprint::print_hash(&swift); + std::print("Zig hash:"); + hexprint::print_hash(&zig); + } + } +} + +func main() { + let timer = Timer::getInstance(); + + let handle = Timer::start(timer); + doWork(); + Timer::stop(timer, handle); + + std::printf("Elapsed time: %lldms\n", Timer::getElapsed(timer, handle)); +} diff --git a/demo/benchmarker/random.d b/demo/benchmarker/random.d new file mode 100644 index 000000000..fc580b112 --- /dev/null +++ b/demo/benchmarker/random.d @@ -0,0 +1,10 @@ +import std.random; + +/// Fill array with random bytes +void fill_random(ubyte* data, size_t len) { + auto rng = Random(unpredictableSeed); + foreach (i; 0 .. len) { + data[i] = cast(ubyte)(rng.front % 256); + rng.popFront(); + } +} diff --git a/demo/benchmarker/setup.sh b/demo/benchmarker/setup.sh new file mode 100755 index 000000000..7df358c2f --- /dev/null +++ b/demo/benchmarker/setup.sh @@ -0,0 +1,2 @@ +export PATH="$(cd ../../build/tools/gluc && pwd):/opt/homebrew/opt/llvm@20/bin/:$PATH" +export GLU_LINKER=/usr/bin/clang++ diff --git a/demo/benchmarker/timer.cpp b/demo/benchmarker/timer.cpp new file mode 100644 index 000000000..30ee42947 --- /dev/null +++ b/demo/benchmarker/timer.cpp @@ -0,0 +1,161 @@ +#include +#include + +using Clock = std::chrono::steady_clock; +using TimePoint = std::chrono::time_point; +using Duration = std::chrono::milliseconds; + +// Structure to represent a timer interval +struct TimerInterval { + TimePoint startTime; + TimePoint endTime; + bool _isRunning; + + TimerInterval(); + + /// @brief Mark the interval as complete + void stop(); + + /// @brief Check if the interval is still running + /// @return True if active, false otherwise + bool isRunning() const; + + /// @brief Get the elapsed time for this interval + /// @return Duration in milliseconds + Duration getElapsed() const; +}; + +/// @brief Singleton timer class for tracking time intervals +class Timer { + std::vector _intervals; + + Timer(); + +public: + Timer(Timer const &) = delete; + Timer &operator=(Timer const &) = delete; + + /// @brief Get the singleton instance + /// @return Reference to the global Timer instance + static Timer &getInstance(); + + /// @brief Start a new timer interval + /// @return Index of the newly created interval + size_t start(); + + /// @brief Stop a specific timer interval + /// @param index The index of the interval to stop + /// @return True if interval was found and stopped, false otherwise + bool stop(size_t index); + + /// @brief Get elapsed time for a specific interval + /// @param index The index of the interval + /// @return Duration in milliseconds, or 0 if index is invalid + Duration getElapsed(size_t index) const; + + /// @brief Get total elapsed time across all intervals + /// @return Total duration in milliseconds + Duration getTotalElapsed() const; + + /// @brief Count currently active intervals + /// @return Number of running intervals + size_t countActive() const; + + /// @brief Get all intervals + /// @return Vector of all timer intervals + std::vector const &getIntervals() const; + + /// @brief Reset all intervals + void reset(); +}; + +// TimerInterval method definitions +TimerInterval::TimerInterval() + : startTime(Clock::now()), endTime(), _isRunning(true) +{ +} + +void TimerInterval::stop() +{ + endTime = Clock::now(); + _isRunning = false; +} + +bool TimerInterval::isRunning() const +{ + return _isRunning; +} + +Duration TimerInterval::getElapsed() const +{ + TimePoint end = _isRunning ? Clock::now() : endTime; + return std::chrono::duration_cast(end - startTime); +} + +// Timer method definitions +Timer::Timer() { } + +Timer &Timer::getInstance() +{ + static Timer *_instance = nullptr; + if (!_instance) { + _instance = new Timer(); + } + return *_instance; +} + +size_t Timer::start() +{ + _intervals.emplace_back(); + return _intervals.size() - 1; +} + +bool Timer::stop(size_t index) +{ + if (index >= _intervals.size()) { + return false; + } + if (!_intervals[index].isRunning()) { + return false; + } + _intervals[index].stop(); + return true; +} + +Duration Timer::getElapsed(size_t index) const +{ + if (index >= _intervals.size()) { + return Duration(0); + } + return _intervals[index].getElapsed(); +} + +Duration Timer::getTotalElapsed() const +{ + Duration total(0); + for (auto const &interval : _intervals) { + total += interval.getElapsed(); + } + return total; +} + +size_t Timer::countActive() const +{ + size_t count = 0; + for (auto const &interval : _intervals) { + if (interval.isRunning()) { + count++; + } + } + return count; +} + +std::vector const &Timer::getIntervals() const +{ + return _intervals; +} + +void Timer::reset() +{ + _intervals.clear(); +} diff --git a/demo/comparison/via-c/Makefile b/demo/comparison/via-c/Makefile new file mode 100644 index 000000000..5fdc4f94f --- /dev/null +++ b/demo/comparison/via-c/Makefile @@ -0,0 +1,34 @@ +.PHONY: all clean run + +CC = clang +DC = ldc2 +ZIG = zig + +CFLAGS = -O3 +DFLAGS = -O3 +ZIGFLAGS = -O ReleaseFast + +TARGET = a.out +C_SRC = main.c +D_SRC = random.d +ZIG_SRC = compute.zig + +D_OBJ = random.o +ZIG_OBJ = compute.o + +all: $(TARGET) + +$(D_OBJ): $(D_SRC) + $(DC) $(DFLAGS) -c $(D_SRC) -of=$(D_OBJ) + +$(ZIG_OBJ): $(ZIG_SRC) + $(ZIG) build-obj $(ZIG_SRC) $(ZIGFLAGS) -femit-bin=$(ZIG_OBJ) + +$(TARGET): $(C_SRC) $(D_OBJ) $(ZIG_OBJ) + $(DC) $(C_SRC) $(D_OBJ) $(ZIG_OBJ) -of=$(TARGET) -L-w + +run: $(TARGET) + time ./$(TARGET) + +clean: + rm -f $(TARGET) $(D_OBJ) $(ZIG_OBJ) diff --git a/demo/comparison/via-c/compute.zig b/demo/comparison/via-c/compute.zig new file mode 100644 index 000000000..0ce18bc88 --- /dev/null +++ b/demo/comparison/via-c/compute.zig @@ -0,0 +1,19 @@ +const std = @import("std"); +const sha256 = std.crypto.hash.sha2.Sha256; + +/// Compute SHA256 hash of data +export fn hashBytes(data: [*]const u8, len: usize, output: *[32]u8) void { + sha256.hash(data[0..len], output, .{}); +} + +/// Verify if data matches expected hash +export fn verifyHash(data: [*]const u8, len: usize, expected: *const [32]u8) bool { + var actual: [32]u8 = undefined; + sha256.hash(data[0..len], &actual, .{}); + return std.mem.eql(u8, &actual, expected); +} + +/// Compare two hashes for equality +export fn compareHashes(hash1: *const [32]u8, hash2: *const [32]u8) bool { + return std.mem.eql(u8, hash1, hash2); +} diff --git a/demo/comparison/via-c/main.c b/demo/comparison/via-c/main.c new file mode 100644 index 000000000..7c06e7b56 --- /dev/null +++ b/demo/comparison/via-c/main.c @@ -0,0 +1,22 @@ +#include +#include + +// External function declarations from Zig (compute.zig) +extern void hashBytes(uint8_t const *data, size_t len, uint8_t output[32]); + +// External function declarations from D (random.d) +extern void fill_random(uint8_t *data, size_t len); + +int main() +{ + for (int i = 0; i < 1000000; i++) { + uint8_t data[100]; + size_t length = 100; + uint8_t hash[32] = { 0 }; + + fill_random(data, length); + hashBytes(data, length, hash); + } + + return 0; +} diff --git a/demo/comparison/via-c/random.d b/demo/comparison/via-c/random.d new file mode 100644 index 000000000..5aa2bcb23 --- /dev/null +++ b/demo/comparison/via-c/random.d @@ -0,0 +1,10 @@ +import std.random; + +/// Fill array with random bytes +extern(C) void fill_random(ubyte* data, size_t len) { + auto rng = Random(unpredictableSeed); + foreach (i; 0 .. len) { + data[i] = cast(ubyte)(rng.front % 256); + rng.popFront(); + } +} diff --git a/demo/comparison/via-glu/compute.zig b/demo/comparison/via-glu/compute.zig new file mode 100644 index 000000000..0ce18bc88 --- /dev/null +++ b/demo/comparison/via-glu/compute.zig @@ -0,0 +1,19 @@ +const std = @import("std"); +const sha256 = std.crypto.hash.sha2.Sha256; + +/// Compute SHA256 hash of data +export fn hashBytes(data: [*]const u8, len: usize, output: *[32]u8) void { + sha256.hash(data[0..len], output, .{}); +} + +/// Verify if data matches expected hash +export fn verifyHash(data: [*]const u8, len: usize, expected: *const [32]u8) bool { + var actual: [32]u8 = undefined; + sha256.hash(data[0..len], &actual, .{}); + return std.mem.eql(u8, &actual, expected); +} + +/// Compare two hashes for equality +export fn compareHashes(hash1: *const [32]u8, hash2: *const [32]u8) bool { + return std.mem.eql(u8, hash1, hash2); +} diff --git a/demo/comparison/via-glu/main.glu b/demo/comparison/via-glu/main.glu new file mode 100644 index 000000000..820cc769b --- /dev/null +++ b/demo/comparison/via-glu/main.glu @@ -0,0 +1,14 @@ + +import compute; +import random::random; + +func main() { + for i in 0..<1000000 { + var data: UInt8[100]; + let length: UInt64 = 100; + var hash: UInt8[32] = {0}; + + random::fill_random(data, length); + compute::hashBytes(data, length, &hash); + } +} diff --git a/demo/comparison/via-glu/random.d b/demo/comparison/via-glu/random.d new file mode 100644 index 000000000..fc580b112 --- /dev/null +++ b/demo/comparison/via-glu/random.d @@ -0,0 +1,10 @@ +import std.random; + +/// Fill array with random bytes +void fill_random(ubyte* data, size_t len) { + auto rng = Random(unpredictableSeed); + foreach (i; 0 .. len) { + data[i] = cast(ubyte)(rng.front % 256); + rng.popFront(); + } +} diff --git a/demo/csfml/build.sh b/demo/csfml/build.sh new file mode 100755 index 000000000..6bf95322c --- /dev/null +++ b/demo/csfml/build.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +SFML_INCLUDE="${SFML_INCLUDE:-/opt/homebrew/include}" +SFML_LIB="${SFML_LIB:-/opt/homebrew/lib}" + +export CPATH="$SFML_INCLUDE:$CPATH" +export GLU_LINKER="${GLU_LINKER:-clang++}" +xcrun gluc "./link.glu" \ + -Wl,-L"$SFML_LIB" \ + -Wl,-lsfml-graphics \ + -Wl,-lsfml-window \ + -Wl,-lsfml-system diff --git a/demo/csfml/jitter.d b/demo/csfml/jitter.d new file mode 100644 index 000000000..56cebe5db --- /dev/null +++ b/demo/csfml/jitter.d @@ -0,0 +1,15 @@ +module jitter; + +void jitter(uint seed, float strength, float* out_xy) { + if (out_xy is null) { + return; + } + + uint s = seed * 1664525u + 1013904223u; + float dx = (cast(float)(s & 0xFF) / 255.0f - 0.5f) * strength; + s = s * 1664525u + 1013904223u; + float dy = (cast(float)((s >> 8) & 0xFF) / 255.0f - 0.5f) * strength; + + out_xy[0] = dx; + out_xy[1] = dy; +} diff --git a/demo/csfml/link.glu b/demo/csfml/link.glu new file mode 100644 index 000000000..9a8caecef --- /dev/null +++ b/demo/csfml/link.glu @@ -0,0 +1,10 @@ +@file_extension("cpp") import sfml; + +@file_extension("zig") import motion::updateMotion; +@file_extension("rs") import palette::palette::color_from_frame; +@file_extension("d") import jitter::jitter::jitter; + +@implement import sfml::glu_update_motion as updateMotion; +@implement import sfml::glu_color_from_frame as color_from_frame; +@implement import sfml::glu_jitter as jitter; + diff --git a/demo/csfml/motion.zig b/demo/csfml/motion.zig new file mode 100644 index 000000000..a70341bdc --- /dev/null +++ b/demo/csfml/motion.zig @@ -0,0 +1,40 @@ +/// Update position/velocity with simple edge bouncing. +export fn updateMotion( + pos: [*]f32, + vel: [*]f32, + bounds: [*]const f32, + radius: f32, + dt: f32, +) void { + var x = pos[0]; + var y = pos[1]; + var vx = vel[0]; + var vy = vel[1]; + + x += vx * dt; + y += vy * dt; + + const max_x = bounds[0] - radius * 2.0; + const max_y = bounds[1] - radius * 2.0; + + if (x <= 0.0) { + x = 0.0; + vx = -vx; + } else if (x >= max_x) { + x = max_x; + vx = -vx; + } + + if (y <= 0.0) { + y = 0.0; + vy = -vy; + } else if (y >= max_y) { + y = max_y; + vy = -vy; + } + + pos[0] = x; + pos[1] = y; + vel[0] = vx; + vel[1] = vy; +} diff --git a/demo/csfml/palette.rs b/demo/csfml/palette.rs new file mode 100644 index 000000000..a90ae9544 --- /dev/null +++ b/demo/csfml/palette.rs @@ -0,0 +1,20 @@ +#![no_main] + +#[no_mangle] +pub fn color_from_frame(frame: u32, out_rgba: *mut u8) { + if out_rgba.is_null() { + return; + } + + let t = frame as f32 * 0.025; + let r = (t.sin() * 127.0 + 128.0) as u8; + let g = ((t + 2.0943952).sin() * 127.0 + 128.0) as u8; + let b = ((t + 4.1887903).sin() * 127.0 + 128.0) as u8; + + unsafe { + *out_rgba.add(0) = r; + *out_rgba.add(1) = g; + *out_rgba.add(2) = b; + *out_rgba.add(3) = 255; + } +} diff --git a/demo/csfml/sfml.cpp b/demo/csfml/sfml.cpp new file mode 100644 index 000000000..4f8a58ef4 --- /dev/null +++ b/demo/csfml/sfml.cpp @@ -0,0 +1,57 @@ +#include "sfml.hpp" + +#include + +#include + +int main() +{ + constexpr unsigned int width = 800; + constexpr unsigned int height = 600; + constexpr float radius = 28.0f; + constexpr float dt = 1.0f / 60.0f; + constexpr float jitter_strength = 3.5f; + + sf::RenderWindow window( + sf::VideoMode(sf::Vector2u { width, height }), "Glu + SFML demo" + ); + window.setFramerateLimit(60); + + sf::CircleShape circle(radius); + + std::array pos = { 120.0f, 160.0f }; + std::array vel = { 180.0f, 140.0f }; + std::array bounds + = { static_cast(width), static_cast(height) }; + + std::array rgba = { 255, 255, 255, 255 }; + sf::Color const background(18, 20, 28, 255); + uint32_t frame = 0; + + while (window.isOpen()) { + while (auto const event = window.pollEvent()) { + if (event->is()) { + window.close(); + } + } + + glu_update_motion(pos.data(), vel.data(), bounds.data(), radius, dt); + glu_color_from_frame(frame, rgba.data()); + + float wiggle[2] = { 0.0f, 0.0f }; + glu_jitter(frame, jitter_strength, wiggle); + + circle.setPosition( + sf::Vector2f { pos[0] + wiggle[0], pos[1] + wiggle[1] } + ); + circle.setFillColor(sf::Color(rgba[0], rgba[1], rgba[2], rgba[3])); + + window.clear(background); + window.draw(circle); + window.display(); + + frame += 1; + } + + return 0; +} diff --git a/demo/csfml/sfml.hpp b/demo/csfml/sfml.hpp new file mode 100644 index 000000000..21596cbc5 --- /dev/null +++ b/demo/csfml/sfml.hpp @@ -0,0 +1,20 @@ +#ifndef GLU_DEMO_SFML_HOST_H +#define GLU_DEMO_SFML_HOST_H + +#include + +#if defined(__GNUC__) + #define GLU_WEAK __attribute__((weak)) +#else + #define GLU_WEAK +#endif + +GLU_WEAK void glu_update_motion( + float *pos_xy, float *vel_xy, float *bounds_xy, float radius, float dt +) +{ +} +GLU_WEAK void glu_color_from_frame(uint32_t frame, uint8_t *out_rgba) { } +GLU_WEAK void glu_jitter(uint32_t frame, float strength, float *out_xy) { } + +#endif diff --git a/lib/ClangImporter/DeclImporter.cpp b/lib/ClangImporter/DeclImporter.cpp index 19d0b1f95..5f7935af0 100644 --- a/lib/ClangImporter/DeclImporter.cpp +++ b/lib/ClangImporter/DeclImporter.cpp @@ -101,4 +101,10 @@ bool DeclImporter::VisitEnumDecl(clang::EnumDecl *enumDecl) return true; } +bool DeclImporter::VisitTypedefNameDecl(clang::TypedefNameDecl *typedefDecl) +{ + _typeConverter.importTypedefDecl(typedefDecl); + return true; +} + } // namespace glu::clangimporter diff --git a/lib/ClangImporter/DeclImporter.hpp b/lib/ClangImporter/DeclImporter.hpp index 6c84f2c80..e44444bfe 100644 --- a/lib/ClangImporter/DeclImporter.hpp +++ b/lib/ClangImporter/DeclImporter.hpp @@ -21,6 +21,7 @@ class DeclImporter : public clang::RecursiveASTVisitor { bool VisitFunctionDecl(clang::FunctionDecl *funcDecl); bool VisitRecordDecl(clang::RecordDecl *recordDecl); bool VisitEnumDecl(clang::EnumDecl *enumDecl); + bool VisitTypedefNameDecl(clang::TypedefNameDecl *typedefDecl); }; } // namespace glu::clangimporter diff --git a/lib/ClangImporter/TypeConverter.cpp b/lib/ClangImporter/TypeConverter.cpp index c4f6aaf71..c32b67ef5 100644 --- a/lib/ClangImporter/TypeConverter.cpp +++ b/lib/ClangImporter/TypeConverter.cpp @@ -64,7 +64,8 @@ glu::types::TypeBase *TypeConverter::convert(clang::QualType clangType) } glu::types::TypeBase *TypeConverter::importRecordDecl( - clang::RecordDecl *recordDecl, bool allowIncomplete + clang::RecordDecl *recordDecl, bool allowIncomplete, + llvm::StringRef forcedName ) { if (!recordDecl) { @@ -75,17 +76,18 @@ glu::types::TypeBase *TypeConverter::importRecordDecl( recordDecl = definition; } - // Skip anonymous structs for now - if (!recordDecl->getIdentifier()) { - return nullptr; - } - auto *canonicalType = _ctx.clang->getRecordType(recordDecl).getCanonicalType().getTypePtr(); if (auto cached = _ctx.typeCache.lookup(canonicalType)) { return cached; } + bool hasName = recordDecl->getIdentifier() != nullptr; + if (!hasName && forcedName.empty()) { + // Skip anonymous structs unless a typedef name is provided. + return nullptr; + } + bool isComplete = recordDecl->isCompleteDefinition(); if (!allowIncomplete && !isComplete) { return nullptr; @@ -121,7 +123,8 @@ glu::types::TypeBase *TypeConverter::importRecordDecl( } auto structLoc = _ctx.translateSourceLocation(recordDecl->getLocation()); - llvm::StringRef structName = copyString(recordDecl->getName(), allocator); + llvm::StringRef structName = hasName ? recordDecl->getName() : forcedName; + structName = copyString(structName, allocator); auto *structDecl = glu::ast::StructDecl::create( allocator, _ctx.glu, structLoc, nullptr, structName, fields, nullptr, glu::ast::Visibility::Public, nullptr @@ -152,8 +155,9 @@ TypeConverter::convertRecordType(clang::RecordType const *type) return importRecordDecl(type->getDecl(), true); } -glu::types::TypeBase * -TypeConverter::importEnumDecl(clang::EnumDecl *enumDecl, bool allowIncomplete) +glu::types::TypeBase *TypeConverter::importEnumDecl( + clang::EnumDecl *enumDecl, bool allowIncomplete, llvm::StringRef forcedName +) { if (!enumDecl) { return nullptr; @@ -163,17 +167,18 @@ TypeConverter::importEnumDecl(clang::EnumDecl *enumDecl, bool allowIncomplete) enumDecl = definition; } - // Skip anonymous enums for now - if (!enumDecl->getIdentifier()) { - return nullptr; - } - auto *canonicalType = _ctx.clang->getEnumType(enumDecl).getCanonicalType().getTypePtr(); if (auto cached = _ctx.typeCache.lookup(canonicalType)) { return cached; } + bool hasName = enumDecl->getIdentifier() != nullptr; + if (!hasName && forcedName.empty()) { + // Skip anonymous enums unless a typedef name is provided. + return nullptr; + } + bool isComplete = enumDecl->isCompleteDefinition(); if (!allowIncomplete && !isComplete) { return nullptr; @@ -202,7 +207,8 @@ TypeConverter::importEnumDecl(clang::EnumDecl *enumDecl, bool allowIncomplete) = isComplete ? convert(enumDecl->getIntegerType()) : nullptr; auto enumLoc = _ctx.translateSourceLocation(enumDecl->getLocation()); - llvm::StringRef enumName = copyString(enumDecl->getName(), allocator); + llvm::StringRef enumName = hasName ? enumDecl->getName() : forcedName; + enumName = copyString(enumName, allocator); auto *gluEnumDecl = glu::ast::EnumDecl::create( allocator, _ctx.glu, enumLoc, nullptr, enumName, cases, underlyingType, glu::ast::Visibility::Public, nullptr @@ -215,6 +221,38 @@ TypeConverter::importEnumDecl(clang::EnumDecl *enumDecl, bool allowIncomplete) return enumType; } +glu::types::TypeBase * +TypeConverter::importTypedefDecl(clang::TypedefNameDecl *typedefDecl) +{ + if (!typedefDecl) { + return nullptr; + } + + llvm::StringRef typedefName = typedefDecl->getName(); + if (typedefName.empty()) { + return nullptr; + } + + auto underlying = typedefDecl->getUnderlyingType(); + if (auto *recordType = underlying->getAs()) { + auto *recordDecl = recordType->getDecl(); + if (recordDecl && !recordDecl->getIdentifier()) { + return importRecordDecl(recordDecl, true, typedefName); + } + return importRecordDecl(recordDecl, true); + } + + if (auto *enumType = underlying->getAs()) { + auto *enumDecl = enumType->getDecl(); + if (enumDecl && !enumDecl->getIdentifier()) { + return importEnumDecl(enumDecl, true, typedefName); + } + return importEnumDecl(enumDecl, true); + } + + return nullptr; +} + glu::types::TypeBase * TypeConverter::convertEnumType(clang::EnumType const *type) { diff --git a/lib/ClangImporter/TypeConverter.hpp b/lib/ClangImporter/TypeConverter.hpp index cd54f09f9..ae798e945 100644 --- a/lib/ClangImporter/TypeConverter.hpp +++ b/lib/ClangImporter/TypeConverter.hpp @@ -3,9 +3,12 @@ #include "ImporterContext.hpp" +#include + namespace clang { class EnumDecl; class RecordDecl; +class TypedefNameDecl; } // namespace clang #include @@ -20,10 +23,16 @@ class TypeConverter { TypeConverter(ImporterContext &ctx) : _ctx(ctx) { } glu::types::TypeBase *convert(clang::QualType clangType); + glu::types::TypeBase *importRecordDecl( + clang::RecordDecl *recordDecl, bool allowIncomplete, + llvm::StringRef forcedName = {} + ); + glu::types::TypeBase *importEnumDecl( + clang::EnumDecl *enumDecl, bool allowIncomplete, + llvm::StringRef forcedName = {} + ); glu::types::TypeBase * - importRecordDecl(clang::RecordDecl *recordDecl, bool allowIncomplete); - glu::types::TypeBase * - importEnumDecl(clang::EnumDecl *enumDecl, bool allowIncomplete); + importTypedefDecl(clang::TypedefNameDecl *typedefDecl); private: glu::types::TypeBase *convertBuiltinType(clang::BuiltinType const *type); diff --git a/lib/Sema/ConstraintSystem/ConversionVisitor.cpp b/lib/Sema/ConstraintSystem/ConversionVisitor.cpp index fac242e86..ca985699c 100644 --- a/lib/Sema/ConstraintSystem/ConversionVisitor.cpp +++ b/lib/Sema/ConstraintSystem/ConversionVisitor.cpp @@ -192,8 +192,31 @@ class ConversionVisitor : public types::TypeVisitor { } // Implicit pointer conversions are more restrictive - // For now, only allow compatible pointee types (including type - // variables) + // Allow Int8/UInt8/Char pointee types to be considered equivalent + auto *fromPointee = fromPtr->getPointee(); + auto *toPointee = toPtr->getPointee(); + + // Check for Int8/UInt8/Char equivalence + if (!llvm::isa(fromPointee) + && !llvm::isa(toPointee)) { + + auto isCharOrByte = [](types::TypeBase *type) -> bool { + if (llvm::isa(type)) { + return true; + } + if (auto *intType = llvm::dyn_cast(type)) { + return intType->getBitWidth() == 8; // Int8 or UInt8 + } + return false; + }; + + if (isCharOrByte(fromPointee) && isCharOrByte(toPointee)) { + return true; + } + } + + // For other types, only allow compatible pointee types (including + // type variables) return _system->unify( fromPtr->getPointee(), toPtr->getPointee(), _state ); diff --git a/tools/gluc/sources/CompilerDriver.cpp b/tools/gluc/sources/CompilerDriver.cpp index f96bf4240..7bb1991dd 100644 --- a/tools/gluc/sources/CompilerDriver.cpp +++ b/tools/gluc/sources/CompilerDriver.cpp @@ -53,6 +53,11 @@ std::vector CompilerDriver::findImportedObjectFiles() for (auto const &entry : importedFilesMap) { glu::FileID fileID = entry.first; llvm::StringRef filePath = sourceManager->getBufferName(fileID); + if (filePath.ends_with(".h")) { + // Headers are imported for declarations only and should not be + // linked. + continue; + } if (filePath.ends_with(".glu")) { std::string objPath = filePath.str(); objPath.replace(objPath.length() - 4, 4, ".o");