Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions lib/std/Target.zig
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ pub const ve = @import("Target/ve.zig");
pub const wasm = @import("Target/wasm.zig");
pub const x86 = @import("Target/x86.zig");
pub const xtensa = @import("Target/xtensa.zig");
pub const propeller = @import("Target/propeller.zig");

pub const Abi = enum {
none,
Expand Down Expand Up @@ -872,6 +873,9 @@ pub fn toElfMachine(target: Target) std.elf.EM {
.xcore => .XCORE,
.xtensa => .XTENSA,

.propeller1 => .PROPELLER,
.propeller2 => .PROPELLER2,

.nvptx,
.nvptx64,
.spirv,
Expand Down Expand Up @@ -932,6 +936,8 @@ pub fn toCoffMachine(target: Target) std.coff.MachineType {
.wasm64,
.xcore,
.xtensa,
.propeller1,
.propeller2,
=> .UNKNOWN,
};
}
Expand Down Expand Up @@ -1147,6 +1153,8 @@ pub const Cpu = struct {
powerpcle,
powerpc64,
powerpc64le,
propeller1,
propeller2,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've thought about this a bit more and I think I'd prefer representing P2 as a CPU feature. My reasoning is that we actually have precedent for this already; for example, MIPS r6 made many backwards-incompatible changes (e.g. reallocated encodings, removed instructions), yet we don't represent that as a distinct Arch tag. I believe PowerPC has also historically removed features, but I can't remember the exact details there.

But let's just get this merged with this approach for now and I'll do a follow-up PR showing what I have in mind.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've thought about this a bit more and I think I'd prefer representing P2 as a CPU feature. My reasoning is that we actually have precedent for this already; for example, MIPS r6 made many backwards-incompatible changes (e.g. reallocated encodings, removed instructions), yet we don't represent that as a distinct Arch tag. I believe PowerPC has also historically removed features, but I can't remember the exact details there.

This would mean we have no generic cpu then, but only propeller1 and propeller2? They are neither binary compatible nor instruction compatible, it's more like arm vs. aarch64 than arm vs. thumb

This would also mean you can't check on arch but always have to check on the actual CPU in your code as well, so if you ever need to do something for p1 and p2, it would look like this:

switch(builtin.cpu.arch) {
  .propeller => switch(builtin.cpu) {
    &std.Target.propeller.cpus.propeller1 => { ... },
    &std.Target.propeller.cpus.propeller1 => { ... },
  },
  ...
}

I wonder why they made them a CPU/Feature in the gcc port. Probably due to convenience as otherwise you have to use prop1-none-eabi-gcc and prop2-none-eabi-gcc instead of prop-none-eabi-gcc

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would mean we have no generic cpu then, but only propeller1 and propeller2? They are neither binary compatible nor instruction compatible, it's more like arm vs. aarch64 than arm vs. thumb

Yes; this is no different from the MIPS situation. The "generic" CPU would be arbitrarily defined to be propeller1, just as mips32/mips64 (r1) are arbitrarily defined to be the generic CPUs for MIPS.

This would also mean you can't check on arch but always have to check on the actual CPU

You would check the feature flag that the CPU would imply; specifically, propeller2 would imply p2. That currently looks like std.Target.propeller.featureSetHas(target.cpu.features, .p2) which is indeed too verbose, but I have some ideas for making this API more ergonomic that I plan to get to soon. I think something like target.cpu.features.has(.propeller, .p2) should be possible with some light comptime magic.

I wonder why they made them a CPU/Feature in the gcc port.

From what I saw in GCC and binutils, there's still a decent amount of overlap in instruction encodings, and they don't use wholly separate en/decoding tables or anything. In this way, I don't think the arm vs aarch64 comparison quite works because A64 really is an entirely new instruction set from A32.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's wait until we have all toolchains tested if they agree on the C type sizes

Copy link
Member

@andrewrk andrewrk Oct 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one heuristic to determine whether it should be a CPU feature vs a different arch is whether you would want two different self-hosted backend implementations, or whether you would rather have one that checks the CPU feature. If they would share a lot of implementation code, that is a hint that the same CPU arch would make sense.

Another heurisic would be @sizeOf(usize). This is the only reason x86 and x86_64 are not the same CPU arch for example even though 64bit is a CPU feature that can be enabled for x86 (which doesn't make any sense given this way of doing things), and you would definitely use the same backend logic for both.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one heuristic to determine whether it should be a CPU feature vs a different arch is whether you would want two different self-hosted backend implementations, or whether you would rather have one that checks the CPU feature. If they would share a lot of implementation code, that is a hint that the same CPU arch would make sense.

That's a pretty good heuristic and i think i have to take a deep-dive into codegen for that 😁

They both have basically the same idea behind the instruction encoding and style, but the semantics of basically all except the most basic instructions are different.

It starts with RDLONG which can only read Hub memory on P1 and reads continuous memory on P2.

@sizeOf(usize) would be 32 bit with 23 bit padding for 2 of the 3 address spaces so it's kinda hard to say.

But now i'm super tempted to create a self-hosted backend for the two processors... 🤔

Next steps for me is still getting all the compilers up and running, which sadly takes a lot of time :(

riscv32,
riscv64,
s390x,
Expand Down Expand Up @@ -1300,6 +1308,14 @@ pub const Cpu = struct {
};
}

/// Returns if the architecture is a Parallax propeller architecture.
pub inline fn isPropeller(arch: Arch) bool {
return switch (arch) {
.propeller1, .propeller2 => true,
else => false,
};
}

pub fn parseCpuModel(arch: Arch, cpu_name: []const u8) !*const Cpu.Model {
for (arch.allCpuModels()) |cpu| {
if (std.mem.eql(u8, cpu_name, cpu.name)) {
Expand Down Expand Up @@ -1344,6 +1360,8 @@ pub const Cpu = struct {
.loongarch32,
.loongarch64,
.arc,
.propeller1,
.propeller2,
=> .little,

.armeb,
Expand Down Expand Up @@ -1376,6 +1394,10 @@ pub const Cpu = struct {
.input, .output, .uniform => is_spirv,
// TODO this should also check how many flash banks the cpu has
.flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr,

// Propeller address spaces:
.cog, .hub => arch.isPropeller(),
.lut => (arch == .propeller2),
};
}

Expand All @@ -1396,6 +1418,7 @@ pub const Cpu = struct {
.nvptx, .nvptx64 => "nvptx",
.wasm32, .wasm64 => "wasm",
.spirv, .spirv32, .spirv64 => "spirv",
.propeller1, .propeller2 => "propeller",
else => @tagName(arch),
};
}
Expand Down Expand Up @@ -1799,6 +1822,8 @@ pub const DynamicLinker = struct {
.spirv,
.spirv32,
.spirv64,
.propeller1,
.propeller2,
=> none,

// TODO go over each item in this list and either move it to the above list, or
Expand Down Expand Up @@ -1909,6 +1934,8 @@ pub fn ptrBitWidth_cpu_abi(cpu: Cpu, abi: Abi) u16 {
.spirv32,
.loongarch32,
.xtensa,
.propeller1,
.propeller2,
=> 32,

.aarch64,
Expand Down Expand Up @@ -2413,6 +2440,8 @@ pub fn cTypeAlignment(target: Target, c_type: CType) u16 {
.kalimba,
.spu_2,
.xtensa,
.propeller1,
.propeller2,
=> 4,

.amdgcn,
Expand Down Expand Up @@ -2516,6 +2545,8 @@ pub fn cTypePreferredAlignment(target: Target, c_type: CType) u16 {
.kalimba,
.spu_2,
.xtensa,
.propeller1,
.propeller2,
=> 4,

.arc,
Expand Down
20 changes: 20 additions & 0 deletions lib/std/Target/propeller.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const std = @import("../std.zig");
const CpuFeature = std.Target.Cpu.Feature;
const CpuModel = std.Target.Cpu.Model;

pub const Feature = enum {};

pub const featureSet = CpuFeature.FeatureSetFns(Feature).featureSet;
pub const featureSetHas = CpuFeature.FeatureSetFns(Feature).featureSetHas;
pub const featureSetHasAny = CpuFeature.FeatureSetFns(Feature).featureSetHasAny;
pub const featureSetHasAll = CpuFeature.FeatureSetFns(Feature).featureSetHasAll;

pub const all_features: [0]CpuFeature = .{};

pub const cpu = struct {
pub const generic = CpuModel{
.name = "generic",
.llvm_name = null,
.features = featureSet(&[_]Feature{}),
};
};
11 changes: 11 additions & 0 deletions lib/std/builtin.zig
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,17 @@ pub const AddressSpace = enum(u5) {
flash3,
flash4,
flash5,

// Propeller address spaces.

/// This address space only addresses the cog-local ram.
cog,

/// This address space only addresses shared hub ram.
hub,

/// This address space only addresses the "lookup" ram
lut,
};

/// This data structure is used by the Zig language code generation and
Expand Down
8 changes: 8 additions & 0 deletions lib/std/elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,14 @@ pub const EM = enum(u16) {
/// Adapteva's Epiphany architecture
ADAPTEVA_EPIPHANY = 0x1223,

/// Parallax Propeller (P1)
/// This value is an unofficial ELF value used in: https://github.com/parallaxinc/propgcc
PROPELLER = 0x5072,

/// Parallax Propeller 2 (P2)
/// This value is an unofficial ELF value used in: https://github.com/ne75/llvm-project
PROPELLER2 = 300,

_,
};

Expand Down
3 changes: 3 additions & 0 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37672,6 +37672,9 @@ pub fn analyzeAsAddressSpace(
.constant => is_gpu and (ctx == .constant),
// TODO this should also check how many flash banks the cpu has
.flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr,

.cog, .hub => arch.isPropeller(),
.lut => (arch == .propeller2),
};

if (!supported) {
Expand Down
1 change: 1 addition & 0 deletions src/Type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1641,6 +1641,7 @@ pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 {
.avr => 1,
.msp430 => 2,
.xcore => 4,
.propeller1, .propeller2 => 4,

.arm,
.armeb,
Expand Down
2 changes: 2 additions & 0 deletions src/Zcu.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3000,6 +3000,8 @@ pub fn atomicPtrAlignment(
.spirv32,
.loongarch32,
.xtensa,
.propeller1,
.propeller2,
=> 32,

.amdgcn,
Expand Down
7 changes: 6 additions & 1 deletion src/codegen/llvm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![]const u8 {

.kalimba,
.spu_2,
.propeller1,
.propeller2,
=> unreachable, // Gated by hasLlvmSupport().

};
try llvm_triple.appendSlice(llvm_arch);

Expand Down Expand Up @@ -282,7 +285,7 @@ pub fn targetArch(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
.wasm32 => .wasm32,
.wasm64 => .wasm64,
.ve => .ve,
.spu_2 => .UnknownArch,
.propeller1, .propeller2, .spu_2 => .UnknownArch,
};
}

Expand Down Expand Up @@ -12714,6 +12717,8 @@ pub fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void {
// LLVM does does not have a backend for these.
.kalimba,
.spu_2,
.propeller1,
.propeller2,
=> unreachable,
}
}
3 changes: 3 additions & 0 deletions src/codegen/spirv.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1856,6 +1856,9 @@ const NavGen = struct {
.flash3,
.flash4,
.flash5,
.cog,
.lut,
.hub,
=> unreachable,
};
}
Expand Down
2 changes: 2 additions & 0 deletions src/target.zig
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ pub fn hasLlvmSupport(target: std.Target, ofmt: std.Target.ObjectFormat) bool {
// No LLVM backend exists.
.kalimba,
.spu_2,
.propeller1,
.propeller2,
=> false,
};
}
Expand Down