| author | |
| committer | |
| log | 7ec729b3ae9ea368124592d8ef1cef422c51de3b |
| tree | 585c547065d982e21a1923de6a8d41adc9688f9d |
| parent | 31ecf75311f83efb0bc0f108c3a3fb6b4c64e153 |
3 files changed, 335 insertions(+), 599 deletions(-)
src/aro_translate_c.zig+11-293| ... | ... | @@ -10,305 +10,23 @@ const Type = aro.Type; |
| 10 | 10 | const ast = @import("translate_c/ast.zig"); |
| 11 | 11 | const ZigNode = ast.Node; |
| 12 | 12 | const ZigTag = ZigNode.Tag; |
| 13 | const common = @import("translate_c/common.zig"); | |
| 14 | const Error = common.Error; | |
| 15 | const MacroProcessingError = common.MacroProcessingError; | |
| 16 | const TypeError = common.TypeError; | |
| 17 | const TransError = common.TransError; | |
| 18 | const SymbolTable = common.SymbolTable; | |
| 19 | const AliasList = common.AliasList; | |
| 20 | const ResultUsed = common.ResultUsed; | |
| 21 | const Scope = common.ScopeExtra(Context, Type); | |
| 13 | 22 | |
| 14 | const Error = mem.Allocator.Error; | |
| 15 | const TransError = translate_c.TransError; | |
| 16 | const TypeError = translate_c.TypeError; | |
| 17 | const ResultUsed = translate_c.ResultUsed; | |
| 18 | const AliasList = translate_c.AliasList; | |
| 19 | const SymbolTable = translate_c.SymbolTable; | |
| 20 | 23 | pub const Compilation = aro.Compilation; |
| 21 | 24 | |
| 22 | const Scope = struct { | |
| 23 | id: Id, | |
| 24 | parent: ?*Scope, | |
| 25 | ||
| 26 | const Id = enum { | |
| 27 | block, | |
| 28 | root, | |
| 29 | condition, | |
| 30 | loop, | |
| 31 | do_loop, | |
| 32 | }; | |
| 33 | ||
| 34 | /// Used for the scope of condition expressions, for example `if (cond)`. | |
| 35 | /// The block is lazily initialised because it is only needed for rare | |
| 36 | /// cases of comma operators being used. | |
| 37 | const Condition = struct { | |
| 38 | base: Scope, | |
| 39 | block: ?Block = null, | |
| 40 | ||
| 41 | fn getBlockScope(self: *Condition, c: *Context) !*Block { | |
| 42 | if (self.block) |*b| return b; | |
| 43 | self.block = try Block.init(c, &self.base, true); | |
| 44 | return &self.block.?; | |
| 45 | } | |
| 46 | ||
| 47 | fn deinit(self: *Condition) void { | |
| 48 | if (self.block) |*b| b.deinit(); | |
| 49 | } | |
| 50 | }; | |
| 51 | ||
| 52 | /// Represents an in-progress ZigNode.Block. This struct is stack-allocated. | |
| 53 | /// When it is deinitialized, it produces an ZigNode.Block which is allocated | |
| 54 | /// into the main arena. | |
| 55 | const Block = struct { | |
| 56 | base: Scope, | |
| 57 | statements: std.ArrayList(ZigNode), | |
| 58 | variables: AliasList, | |
| 59 | mangle_count: u32 = 0, | |
| 60 | label: ?[]const u8 = null, | |
| 61 | ||
| 62 | /// By default all variables are discarded, since we do not know in advance if they | |
| 63 | /// will be used. This maps the variable's name to the Discard payload, so that if | |
| 64 | /// the variable is subsequently referenced we can indicate that the discard should | |
| 65 | /// be skipped during the intermediate AST -> Zig AST render step. | |
| 66 | variable_discards: std.StringArrayHashMap(*ast.Payload.Discard), | |
| 67 | ||
| 68 | /// When the block corresponds to a function, keep track of the return type | |
| 69 | /// so that the return expression can be cast, if necessary | |
| 70 | return_type: ?Type = null, | |
| 71 | ||
| 72 | /// C static local variables are wrapped in a block-local struct. The struct | |
| 73 | /// is named after the (mangled) variable name, the Zig variable within the | |
| 74 | /// struct itself is given this name. | |
| 75 | const StaticInnerName = "static"; | |
| 76 | ||
| 77 | fn init(c: *Context, parent: *Scope, labeled: bool) !Block { | |
| 78 | var blk = Block{ | |
| 79 | .base = .{ | |
| 80 | .id = .block, | |
| 81 | .parent = parent, | |
| 82 | }, | |
| 83 | .statements = std.ArrayList(ZigNode).init(c.gpa), | |
| 84 | .variables = AliasList.init(c.gpa), | |
| 85 | .variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa), | |
| 86 | }; | |
| 87 | if (labeled) { | |
| 88 | blk.label = try blk.makeMangledName(c, "blk"); | |
| 89 | } | |
| 90 | return blk; | |
| 91 | } | |
| 92 | ||
| 93 | fn deinit(self: *Block) void { | |
| 94 | self.statements.deinit(); | |
| 95 | self.variables.deinit(); | |
| 96 | self.variable_discards.deinit(); | |
| 97 | self.* = undefined; | |
| 98 | } | |
| 99 | ||
| 100 | fn complete(self: *Block, c: *Context) !ZigNode { | |
| 101 | if (self.base.parent.?.id == .do_loop) { | |
| 102 | // We reserve 1 extra statement if the parent is a do_loop. This is in case of | |
| 103 | // do while, we want to put `if (cond) break;` at the end. | |
| 104 | const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .do_loop); | |
| 105 | var stmts = try c.arena.alloc(ZigNode, alloc_len); | |
| 106 | stmts.len = self.statements.items.len; | |
| 107 | @memcpy(stmts[0..self.statements.items.len], self.statements.items); | |
| 108 | return ZigTag.block.create(c.arena, .{ | |
| 109 | .label = self.label, | |
| 110 | .stmts = stmts, | |
| 111 | }); | |
| 112 | } | |
| 113 | if (self.statements.items.len == 0) return ZigTag.empty_block.init(); | |
| 114 | return ZigTag.block.create(c.arena, .{ | |
| 115 | .label = self.label, | |
| 116 | .stmts = try c.arena.dupe(ZigNode, self.statements.items), | |
| 117 | }); | |
| 118 | } | |
| 119 | ||
| 120 | /// Given the desired name, return a name that does not shadow anything from outer scopes. | |
| 121 | /// Inserts the returned name into the scope. | |
| 122 | /// The name will not be visible to callers of getAlias. | |
| 123 | fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 { | |
| 124 | return scope.createMangledName(c, name, true); | |
| 125 | } | |
| 126 | ||
| 127 | /// Same as reserveMangledName, but enables the alias immediately. | |
| 128 | fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 { | |
| 129 | return scope.createMangledName(c, name, false); | |
| 130 | } | |
| 131 | ||
| 132 | fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 { | |
| 133 | const name_copy = try c.arena.dupe(u8, name); | |
| 134 | var proposed_name = name_copy; | |
| 135 | while (scope.contains(proposed_name)) { | |
| 136 | scope.mangle_count += 1; | |
| 137 | proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count }); | |
| 138 | } | |
| 139 | const new_mangle = try scope.variables.addOne(); | |
| 140 | if (reservation) { | |
| 141 | new_mangle.* = .{ .name = name_copy, .alias = name_copy }; | |
| 142 | } else { | |
| 143 | new_mangle.* = .{ .name = name_copy, .alias = proposed_name }; | |
| 144 | } | |
| 145 | return proposed_name; | |
| 146 | } | |
| 147 | ||
| 148 | fn getAlias(scope: *Block, name: []const u8) []const u8 { | |
| 149 | for (scope.variables.items) |p| { | |
| 150 | if (mem.eql(u8, p.name, name)) | |
| 151 | return p.alias; | |
| 152 | } | |
| 153 | return scope.base.parent.?.getAlias(name); | |
| 154 | } | |
| 155 | ||
| 156 | fn localContains(scope: *Block, name: []const u8) bool { | |
| 157 | for (scope.variables.items) |p| { | |
| 158 | if (mem.eql(u8, p.alias, name)) | |
| 159 | return true; | |
| 160 | } | |
| 161 | return false; | |
| 162 | } | |
| 163 | ||
| 164 | fn contains(scope: *Block, name: []const u8) bool { | |
| 165 | if (scope.localContains(name)) | |
| 166 | return true; | |
| 167 | return scope.base.parent.?.contains(name); | |
| 168 | } | |
| 169 | ||
| 170 | fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void { | |
| 171 | const name_node = try ZigTag.identifier.create(c.arena, name); | |
| 172 | const discard = try ZigTag.discard.create(c.arena, .{ .should_skip = false, .value = name_node }); | |
| 173 | try scope.statements.append(discard); | |
| 174 | try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?); | |
| 175 | } | |
| 176 | }; | |
| 177 | ||
| 178 | const Root = struct { | |
| 179 | base: Scope, | |
| 180 | sym_table: SymbolTable, | |
| 181 | macro_table: SymbolTable, | |
| 182 | context: *Context, | |
| 183 | nodes: std.ArrayList(ZigNode), | |
| 184 | ||
| 185 | fn init(c: *Context) Root { | |
| 186 | return .{ | |
| 187 | .base = .{ | |
| 188 | .id = .root, | |
| 189 | .parent = null, | |
| 190 | }, | |
| 191 | .sym_table = SymbolTable.init(c.gpa), | |
| 192 | .macro_table = SymbolTable.init(c.gpa), | |
| 193 | .context = c, | |
| 194 | .nodes = std.ArrayList(ZigNode).init(c.gpa), | |
| 195 | }; | |
| 196 | } | |
| 197 | ||
| 198 | fn deinit(scope: *Root) void { | |
| 199 | scope.sym_table.deinit(); | |
| 200 | scope.macro_table.deinit(); | |
| 201 | scope.nodes.deinit(); | |
| 202 | } | |
| 203 | ||
| 204 | /// Check if the global scope contains this name, without looking into the "future", e.g. | |
| 205 | /// ignore the preprocessed decl and macro names. | |
| 206 | fn containsNow(scope: *Root, name: []const u8) bool { | |
| 207 | return scope.sym_table.contains(name) or scope.macro_table.contains(name); | |
| 208 | } | |
| 209 | ||
| 210 | /// Check if the global scope contains the name, includes all decls that haven't been translated yet. | |
| 211 | fn contains(scope: *Root, name: []const u8) bool { | |
| 212 | return scope.containsNow(name) or scope.context.global_names.contains(name); | |
| 213 | } | |
| 214 | }; | |
| 215 | ||
| 216 | fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block { | |
| 217 | var scope = inner; | |
| 218 | while (true) { | |
| 219 | switch (scope.id) { | |
| 220 | .root => unreachable, | |
| 221 | .block => return @fieldParentPtr(Block, "base", scope), | |
| 222 | .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c), | |
| 223 | else => scope = scope.parent.?, | |
| 224 | } | |
| 225 | } | |
| 226 | } | |
| 227 | ||
| 228 | fn findBlockReturnType(inner: *Scope) Type { | |
| 229 | var scope = inner; | |
| 230 | while (true) { | |
| 231 | switch (scope.id) { | |
| 232 | .root => unreachable, | |
| 233 | .block => { | |
| 234 | const block = @fieldParentPtr(Block, "base", scope); | |
| 235 | if (block.return_type) |qt| return qt; | |
| 236 | scope = scope.parent.?; | |
| 237 | }, | |
| 238 | else => scope = scope.parent.?, | |
| 239 | } | |
| 240 | } | |
| 241 | } | |
| 242 | ||
| 243 | fn getAlias(scope: *Scope, name: []const u8) []const u8 { | |
| 244 | return switch (scope.id) { | |
| 245 | .root => return name, | |
| 246 | .block => @fieldParentPtr(Block, "base", scope).getAlias(name), | |
| 247 | .loop, .do_loop, .condition => scope.parent.?.getAlias(name), | |
| 248 | }; | |
| 249 | } | |
| 250 | ||
| 251 | fn contains(scope: *Scope, name: []const u8) bool { | |
| 252 | return switch (scope.id) { | |
| 253 | .root => @fieldParentPtr(Root, "base", scope).contains(name), | |
| 254 | .block => @fieldParentPtr(Block, "base", scope).contains(name), | |
| 255 | .loop, .do_loop, .condition => scope.parent.?.contains(name), | |
| 256 | }; | |
| 257 | } | |
| 258 | ||
| 259 | fn getBreakableScope(inner: *Scope) *Scope { | |
| 260 | var scope = inner; | |
| 261 | while (true) { | |
| 262 | switch (scope.id) { | |
| 263 | .root => unreachable, | |
| 264 | .loop, .do_loop => return scope, | |
| 265 | else => scope = scope.parent.?, | |
| 266 | } | |
| 267 | } | |
| 268 | } | |
| 269 | ||
| 270 | /// Appends a node to the first block scope if inside a function, or to the root tree if not. | |
| 271 | fn appendNode(inner: *Scope, node: ZigNode) !void { | |
| 272 | var scope = inner; | |
| 273 | while (true) { | |
| 274 | switch (scope.id) { | |
| 275 | .root => { | |
| 276 | const root = @fieldParentPtr(Root, "base", scope); | |
| 277 | return root.nodes.append(node); | |
| 278 | }, | |
| 279 | .block => { | |
| 280 | const block = @fieldParentPtr(Block, "base", scope); | |
| 281 | return block.statements.append(node); | |
| 282 | }, | |
| 283 | else => scope = scope.parent.?, | |
| 284 | } | |
| 285 | } | |
| 286 | } | |
| 287 | ||
| 288 | fn skipVariableDiscard(inner: *Scope, name: []const u8) void { | |
| 289 | var scope = inner; | |
| 290 | while (true) { | |
| 291 | switch (scope.id) { | |
| 292 | .root => return, | |
| 293 | .block => { | |
| 294 | const block = @fieldParentPtr(Block, "base", scope); | |
| 295 | if (block.variable_discards.get(name)) |discard| { | |
| 296 | discard.data.should_skip = true; | |
| 297 | return; | |
| 298 | } | |
| 299 | }, | |
| 300 | else => {}, | |
| 301 | } | |
| 302 | scope = scope.parent.?; | |
| 303 | } | |
| 304 | } | |
| 305 | }; | |
| 306 | ||
| 307 | 25 | const Context = struct { |
| 308 | 26 | gpa: mem.Allocator, |
| 309 | 27 | arena: mem.Allocator, |
| 310 | 28 | decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{}, |
| 311 | alias_list: translate_c.AliasList, | |
| 29 | alias_list: AliasList, | |
| 312 | 30 | global_scope: *Scope.Root, |
| 313 | 31 | mangle_count: u32 = 0, |
| 314 | 32 | /// Table of record decls that have been demoted to opaques. |
| ... | ... | @@ -429,7 +147,7 @@ pub fn translate( |
| 429 | 147 | var context = Context{ |
| 430 | 148 | .gpa = gpa, |
| 431 | 149 | .arena = arena, |
| 432 | .alias_list = translate_c.AliasList.init(gpa), | |
| 150 | .alias_list = AliasList.init(gpa), | |
| 433 | 151 | .global_scope = try arena.create(Scope.Root), |
| 434 | 152 | .pattern_list = try translate_c.PatternList.init(gpa), |
| 435 | 153 | .comp = comp, |
src/translate_c.zig+13-306| ... | ... | @@ -7,312 +7,24 @@ const CToken = std.c.Token; |
| 7 | 7 | const mem = std.mem; |
| 8 | 8 | const math = std.math; |
| 9 | 9 | const meta = std.meta; |
| 10 | const CallingConvention = std.builtin.CallingConvention; | |
| 10 | 11 | const ast = @import("translate_c/ast.zig"); |
| 11 | 12 | const Node = ast.Node; |
| 12 | 13 | const Tag = Node.Tag; |
| 13 | ||
| 14 | const CallingConvention = std.builtin.CallingConvention; | |
| 15 | ||
| 16 | pub const Error = std.mem.Allocator.Error; | |
| 17 | pub const MacroProcessingError = Error || error{UnexpectedMacroToken}; | |
| 18 | pub const TypeError = Error || error{UnsupportedType}; | |
| 19 | pub const TransError = TypeError || error{UnsupportedTranslation}; | |
| 20 | ||
| 21 | pub const SymbolTable = std.StringArrayHashMap(Node); | |
| 22 | pub const AliasList = std.ArrayList(struct { | |
| 23 | alias: []const u8, | |
| 24 | name: []const u8, | |
| 25 | }); | |
| 14 | const common = @import("translate_c/common.zig"); | |
| 15 | const Error = common.Error; | |
| 16 | const MacroProcessingError = common.MacroProcessingError; | |
| 17 | const TypeError = common.TypeError; | |
| 18 | const TransError = common.TransError; | |
| 19 | const SymbolTable = common.SymbolTable; | |
| 20 | const AliasList = common.AliasList; | |
| 21 | const ResultUsed = common.ResultUsed; | |
| 22 | const Scope = common.ScopeExtra(Context, clang.QualType); | |
| 26 | 23 | |
| 27 | 24 | // Maps macro parameter names to token position, for determining if different |
| 28 | 25 | // identifiers refer to the same positional argument in different macros. |
| 29 | 26 | const ArgsPositionMap = std.StringArrayHashMapUnmanaged(usize); |
| 30 | 27 | |
| 31 | const Scope = struct { | |
| 32 | id: Id, | |
| 33 | parent: ?*Scope, | |
| 34 | ||
| 35 | const Id = enum { | |
| 36 | block, | |
| 37 | root, | |
| 38 | condition, | |
| 39 | loop, | |
| 40 | do_loop, | |
| 41 | }; | |
| 42 | ||
| 43 | /// Used for the scope of condition expressions, for example `if (cond)`. | |
| 44 | /// The block is lazily initialised because it is only needed for rare | |
| 45 | /// cases of comma operators being used. | |
| 46 | const Condition = struct { | |
| 47 | base: Scope, | |
| 48 | block: ?Block = null, | |
| 49 | ||
| 50 | fn getBlockScope(self: *Condition, c: *Context) !*Block { | |
| 51 | if (self.block) |*b| return b; | |
| 52 | self.block = try Block.init(c, &self.base, true); | |
| 53 | return &self.block.?; | |
| 54 | } | |
| 55 | ||
| 56 | fn deinit(self: *Condition) void { | |
| 57 | if (self.block) |*b| b.deinit(); | |
| 58 | } | |
| 59 | }; | |
| 60 | ||
| 61 | /// Represents an in-progress Node.Block. This struct is stack-allocated. | |
| 62 | /// When it is deinitialized, it produces an Node.Block which is allocated | |
| 63 | /// into the main arena. | |
| 64 | const Block = struct { | |
| 65 | base: Scope, | |
| 66 | statements: std.ArrayList(Node), | |
| 67 | variables: AliasList, | |
| 68 | mangle_count: u32 = 0, | |
| 69 | label: ?[]const u8 = null, | |
| 70 | ||
| 71 | /// By default all variables are discarded, since we do not know in advance if they | |
| 72 | /// will be used. This maps the variable's name to the Discard payload, so that if | |
| 73 | /// the variable is subsequently referenced we can indicate that the discard should | |
| 74 | /// be skipped during the intermediate AST -> Zig AST render step. | |
| 75 | variable_discards: std.StringArrayHashMap(*ast.Payload.Discard), | |
| 76 | ||
| 77 | /// When the block corresponds to a function, keep track of the return type | |
| 78 | /// so that the return expression can be cast, if necessary | |
| 79 | return_type: ?clang.QualType = null, | |
| 80 | ||
| 81 | /// C static local variables are wrapped in a block-local struct. The struct | |
| 82 | /// is named after the (mangled) variable name, the Zig variable within the | |
| 83 | /// struct itself is given this name. | |
| 84 | const StaticInnerName = "static"; | |
| 85 | ||
| 86 | fn init(c: *Context, parent: *Scope, labeled: bool) !Block { | |
| 87 | var blk = Block{ | |
| 88 | .base = .{ | |
| 89 | .id = .block, | |
| 90 | .parent = parent, | |
| 91 | }, | |
| 92 | .statements = std.ArrayList(Node).init(c.gpa), | |
| 93 | .variables = AliasList.init(c.gpa), | |
| 94 | .variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa), | |
| 95 | }; | |
| 96 | if (labeled) { | |
| 97 | blk.label = try blk.makeMangledName(c, "blk"); | |
| 98 | } | |
| 99 | return blk; | |
| 100 | } | |
| 101 | ||
| 102 | fn deinit(self: *Block) void { | |
| 103 | self.statements.deinit(); | |
| 104 | self.variables.deinit(); | |
| 105 | self.variable_discards.deinit(); | |
| 106 | self.* = undefined; | |
| 107 | } | |
| 108 | ||
| 109 | fn complete(self: *Block, c: *Context) !Node { | |
| 110 | if (self.base.parent.?.id == .do_loop) { | |
| 111 | // We reserve 1 extra statement if the parent is a do_loop. This is in case of | |
| 112 | // do while, we want to put `if (cond) break;` at the end. | |
| 113 | const alloc_len = self.statements.items.len + @intFromBool(self.base.parent.?.id == .do_loop); | |
| 114 | var stmts = try c.arena.alloc(Node, alloc_len); | |
| 115 | stmts.len = self.statements.items.len; | |
| 116 | @memcpy(stmts[0..self.statements.items.len], self.statements.items); | |
| 117 | return Tag.block.create(c.arena, .{ | |
| 118 | .label = self.label, | |
| 119 | .stmts = stmts, | |
| 120 | }); | |
| 121 | } | |
| 122 | if (self.statements.items.len == 0) return Tag.empty_block.init(); | |
| 123 | return Tag.block.create(c.arena, .{ | |
| 124 | .label = self.label, | |
| 125 | .stmts = try c.arena.dupe(Node, self.statements.items), | |
| 126 | }); | |
| 127 | } | |
| 128 | ||
| 129 | /// Given the desired name, return a name that does not shadow anything from outer scopes. | |
| 130 | /// Inserts the returned name into the scope. | |
| 131 | /// The name will not be visible to callers of getAlias. | |
| 132 | fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 { | |
| 133 | return scope.createMangledName(c, name, true); | |
| 134 | } | |
| 135 | ||
| 136 | /// Same as reserveMangledName, but enables the alias immediately. | |
| 137 | fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 { | |
| 138 | return scope.createMangledName(c, name, false); | |
| 139 | } | |
| 140 | ||
| 141 | fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 { | |
| 142 | const name_copy = try c.arena.dupe(u8, name); | |
| 143 | var proposed_name = name_copy; | |
| 144 | while (scope.contains(proposed_name)) { | |
| 145 | scope.mangle_count += 1; | |
| 146 | proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count }); | |
| 147 | } | |
| 148 | const new_mangle = try scope.variables.addOne(); | |
| 149 | if (reservation) { | |
| 150 | new_mangle.* = .{ .name = name_copy, .alias = name_copy }; | |
| 151 | } else { | |
| 152 | new_mangle.* = .{ .name = name_copy, .alias = proposed_name }; | |
| 153 | } | |
| 154 | return proposed_name; | |
| 155 | } | |
| 156 | ||
| 157 | fn getAlias(scope: *Block, name: []const u8) []const u8 { | |
| 158 | for (scope.variables.items) |p| { | |
| 159 | if (mem.eql(u8, p.name, name)) | |
| 160 | return p.alias; | |
| 161 | } | |
| 162 | return scope.base.parent.?.getAlias(name); | |
| 163 | } | |
| 164 | ||
| 165 | fn localContains(scope: *Block, name: []const u8) bool { | |
| 166 | for (scope.variables.items) |p| { | |
| 167 | if (mem.eql(u8, p.alias, name)) | |
| 168 | return true; | |
| 169 | } | |
| 170 | return false; | |
| 171 | } | |
| 172 | ||
| 173 | fn contains(scope: *Block, name: []const u8) bool { | |
| 174 | if (scope.localContains(name)) | |
| 175 | return true; | |
| 176 | return scope.base.parent.?.contains(name); | |
| 177 | } | |
| 178 | ||
| 179 | fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void { | |
| 180 | const name_node = try Tag.identifier.create(c.arena, name); | |
| 181 | const discard = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = name_node }); | |
| 182 | try scope.statements.append(discard); | |
| 183 | try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?); | |
| 184 | } | |
| 185 | }; | |
| 186 | ||
| 187 | const Root = struct { | |
| 188 | base: Scope, | |
| 189 | sym_table: SymbolTable, | |
| 190 | macro_table: SymbolTable, | |
| 191 | context: *Context, | |
| 192 | nodes: std.ArrayList(Node), | |
| 193 | ||
| 194 | fn init(c: *Context) Root { | |
| 195 | return .{ | |
| 196 | .base = .{ | |
| 197 | .id = .root, | |
| 198 | .parent = null, | |
| 199 | }, | |
| 200 | .sym_table = SymbolTable.init(c.gpa), | |
| 201 | .macro_table = SymbolTable.init(c.gpa), | |
| 202 | .context = c, | |
| 203 | .nodes = std.ArrayList(Node).init(c.gpa), | |
| 204 | }; | |
| 205 | } | |
| 206 | ||
| 207 | fn deinit(scope: *Root) void { | |
| 208 | scope.sym_table.deinit(); | |
| 209 | scope.macro_table.deinit(); | |
| 210 | scope.nodes.deinit(); | |
| 211 | } | |
| 212 | ||
| 213 | /// Check if the global scope contains this name, without looking into the "future", e.g. | |
| 214 | /// ignore the preprocessed decl and macro names. | |
| 215 | fn containsNow(scope: *Root, name: []const u8) bool { | |
| 216 | return scope.sym_table.contains(name) or scope.macro_table.contains(name); | |
| 217 | } | |
| 218 | ||
| 219 | /// Check if the global scope contains the name, includes all decls that haven't been translated yet. | |
| 220 | fn contains(scope: *Root, name: []const u8) bool { | |
| 221 | return scope.containsNow(name) or scope.context.global_names.contains(name) or scope.context.weak_global_names.contains(name); | |
| 222 | } | |
| 223 | }; | |
| 224 | ||
| 225 | fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block { | |
| 226 | var scope = inner; | |
| 227 | while (true) { | |
| 228 | switch (scope.id) { | |
| 229 | .root => unreachable, | |
| 230 | .block => return @fieldParentPtr(Block, "base", scope), | |
| 231 | .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c), | |
| 232 | else => scope = scope.parent.?, | |
| 233 | } | |
| 234 | } | |
| 235 | } | |
| 236 | ||
| 237 | fn findBlockReturnType(inner: *Scope) clang.QualType { | |
| 238 | var scope = inner; | |
| 239 | while (true) { | |
| 240 | switch (scope.id) { | |
| 241 | .root => unreachable, | |
| 242 | .block => { | |
| 243 | const block = @fieldParentPtr(Block, "base", scope); | |
| 244 | if (block.return_type) |qt| return qt; | |
| 245 | scope = scope.parent.?; | |
| 246 | }, | |
| 247 | else => scope = scope.parent.?, | |
| 248 | } | |
| 249 | } | |
| 250 | } | |
| 251 | ||
| 252 | fn getAlias(scope: *Scope, name: []const u8) []const u8 { | |
| 253 | return switch (scope.id) { | |
| 254 | .root => return name, | |
| 255 | .block => @fieldParentPtr(Block, "base", scope).getAlias(name), | |
| 256 | .loop, .do_loop, .condition => scope.parent.?.getAlias(name), | |
| 257 | }; | |
| 258 | } | |
| 259 | ||
| 260 | fn contains(scope: *Scope, name: []const u8) bool { | |
| 261 | return switch (scope.id) { | |
| 262 | .root => @fieldParentPtr(Root, "base", scope).contains(name), | |
| 263 | .block => @fieldParentPtr(Block, "base", scope).contains(name), | |
| 264 | .loop, .do_loop, .condition => scope.parent.?.contains(name), | |
| 265 | }; | |
| 266 | } | |
| 267 | ||
| 268 | fn getBreakableScope(inner: *Scope) *Scope { | |
| 269 | var scope = inner; | |
| 270 | while (true) { | |
| 271 | switch (scope.id) { | |
| 272 | .root => unreachable, | |
| 273 | .loop, .do_loop => return scope, | |
| 274 | else => scope = scope.parent.?, | |
| 275 | } | |
| 276 | } | |
| 277 | } | |
| 278 | ||
| 279 | /// Appends a node to the first block scope if inside a function, or to the root tree if not. | |
| 280 | fn appendNode(inner: *Scope, node: Node) !void { | |
| 281 | var scope = inner; | |
| 282 | while (true) { | |
| 283 | switch (scope.id) { | |
| 284 | .root => { | |
| 285 | const root = @fieldParentPtr(Root, "base", scope); | |
| 286 | return root.nodes.append(node); | |
| 287 | }, | |
| 288 | .block => { | |
| 289 | const block = @fieldParentPtr(Block, "base", scope); | |
| 290 | return block.statements.append(node); | |
| 291 | }, | |
| 292 | else => scope = scope.parent.?, | |
| 293 | } | |
| 294 | } | |
| 295 | } | |
| 296 | ||
| 297 | fn skipVariableDiscard(inner: *Scope, name: []const u8) void { | |
| 298 | var scope = inner; | |
| 299 | while (true) { | |
| 300 | switch (scope.id) { | |
| 301 | .root => return, | |
| 302 | .block => { | |
| 303 | const block = @fieldParentPtr(Block, "base", scope); | |
| 304 | if (block.variable_discards.get(name)) |discard| { | |
| 305 | discard.data.should_skip = true; | |
| 306 | return; | |
| 307 | } | |
| 308 | }, | |
| 309 | else => {}, | |
| 310 | } | |
| 311 | scope = scope.parent.?; | |
| 312 | } | |
| 313 | } | |
| 314 | }; | |
| 315 | ||
| 316 | 28 | pub const Context = struct { |
| 317 | 29 | gpa: mem.Allocator, |
| 318 | 30 | arena: mem.Allocator, |
| ... | ... | @@ -829,7 +541,7 @@ fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType, |
| 829 | 541 | /// var static = S.*; |
| 830 | 542 | /// }).static; |
| 831 | 543 | fn stringLiteralToCharStar(c: *Context, str: Node) Error!Node { |
| 832 | const var_name = Scope.Block.StaticInnerName; | |
| 544 | const var_name = Scope.Block.static_inner_name; | |
| 833 | 545 | |
| 834 | 546 | const variables = try c.arena.alloc(Node, 1); |
| 835 | 547 | variables[0] = try Tag.mut_str.create(c.arena, .{ .name = var_name, .init = str }); |
| ... | ... | @@ -1423,11 +1135,6 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E |
| 1423 | 1135 | } |
| 1424 | 1136 | } |
| 1425 | 1137 | |
| 1426 | pub const ResultUsed = enum { | |
| 1427 | used, | |
| 1428 | unused, | |
| 1429 | }; | |
| 1430 | ||
| 1431 | 1138 | fn transStmt( |
| 1432 | 1139 | c: *Context, |
| 1433 | 1140 | scope: *Scope, |
| ... | ... | @@ -2070,7 +1777,7 @@ fn transDeclStmtOne( |
| 2070 | 1777 | init_node = try removeCVQualifiers(c, dst_type_node, init_node); |
| 2071 | 1778 | } |
| 2072 | 1779 | |
| 2073 | const var_name: []const u8 = if (is_static_local) Scope.Block.StaticInnerName else mangled_name; | |
| 1780 | const var_name: []const u8 = if (is_static_local) Scope.Block.static_inner_name else mangled_name; | |
| 2074 | 1781 | var node = try Tag.var_decl.create(c.arena, .{ |
| 2075 | 1782 | .is_pub = false, |
| 2076 | 1783 | .is_const = is_const, |
| ... | ... | @@ -2153,7 +1860,7 @@ fn transDeclRefExpr( |
| 2153 | 1860 | if (var_decl.isStaticLocal()) { |
| 2154 | 1861 | ref_expr = try Tag.field_access.create(c.arena, .{ |
| 2155 | 1862 | .lhs = ref_expr, |
| 2156 | .field_name = Scope.Block.StaticInnerName, | |
| 1863 | .field_name = Scope.Block.static_inner_name, | |
| 2157 | 1864 | }); |
| 2158 | 1865 | } |
| 2159 | 1866 | } |
src/translate_c/common.zig created+311| ... | ... | @@ -0,0 +1,311 @@ |
| 1 | const std = @import("std"); | |
| 2 | const ast = @import("ast.zig"); | |
| 3 | const Node = ast.Node; | |
| 4 | const Tag = Node.Tag; | |
| 5 | ||
| 6 | const CallingConvention = std.builtin.CallingConvention; | |
| 7 | ||
| 8 | pub const Error = std.mem.Allocator.Error; | |
| 9 | pub const MacroProcessingError = Error || error{UnexpectedMacroToken}; | |
| 10 | pub const TypeError = Error || error{UnsupportedType}; | |
| 11 | pub const TransError = TypeError || error{UnsupportedTranslation}; | |
| 12 | ||
| 13 | pub const SymbolTable = std.StringArrayHashMap(Node); | |
| 14 | pub const AliasList = std.ArrayList(struct { | |
| 15 | alias: []const u8, | |
| 16 | name: []const u8, | |
| 17 | }); | |
| 18 | ||
| 19 | pub const ResultUsed = enum { | |
| 20 | used, | |
| 21 | unused, | |
| 22 | }; | |
| 23 | ||
| 24 | pub fn ScopeExtra(comptime Context: type, comptime Type: type) type { | |
| 25 | return struct { | |
| 26 | id: Id, | |
| 27 | parent: ?*Scope, | |
| 28 | ||
| 29 | const Scope = @This(); | |
| 30 | ||
| 31 | pub const Id = enum { | |
| 32 | block, | |
| 33 | root, | |
| 34 | condition, | |
| 35 | loop, | |
| 36 | do_loop, | |
| 37 | }; | |
| 38 | ||
| 39 | /// Used for the scope of condition expressions, for example `if (cond)`. | |
| 40 | /// The block is lazily initialised because it is only needed for rare | |
| 41 | /// cases of comma operators being used. | |
| 42 | pub const Condition = struct { | |
| 43 | base: Scope, | |
| 44 | block: ?Block = null, | |
| 45 | ||
| 46 | pub fn getBlockScope(self: *Condition, c: *Context) !*Block { | |
| 47 | if (self.block) |*b| return b; | |
| 48 | self.block = try Block.init(c, &self.base, true); | |
| 49 | return &self.block.?; | |
| 50 | } | |
| 51 | ||
| 52 | pub fn deinit(self: *Condition) void { | |
| 53 | if (self.block) |*b| b.deinit(); | |
| 54 | } | |
| 55 | }; | |
| 56 | ||
| 57 | /// Represents an in-progress Node.Block. This struct is stack-allocated. | |
| 58 | /// When it is deinitialized, it produces an Node.Block which is allocated | |
| 59 | /// into the main arena. | |
| 60 | pub const Block = struct { | |
| 61 | base: Scope, | |
| 62 | statements: std.ArrayList(Node), | |
| 63 | variables: AliasList, | |
| 64 | mangle_count: u32 = 0, | |
| 65 | label: ?[]const u8 = null, | |
| 66 | ||
| 67 | /// By default all variables are discarded, since we do not know in advance if they | |
| 68 | /// will be used. This maps the variable's name to the Discard payload, so that if | |
| 69 | /// the variable is subsequently referenced we can indicate that the discard should | |
| 70 | /// be skipped during the intermediate AST -> Zig AST render step. | |
| 71 | variable_discards: std.StringArrayHashMap(*ast.Payload.Discard), | |
| 72 | ||
| 73 | /// When the block corresponds to a function, keep track of the return type | |
| 74 | /// so that the return expression can be cast, if necessary | |
| 75 | return_type: ?Type = null, | |
| 76 | ||
| 77 | /// C static local variables are wrapped in a block-local struct. The struct | |
| 78 | /// is named after the (mangled) variable name, the Zig variable within the | |
| 79 | /// struct itself is given this name. | |
| 80 | pub const static_inner_name = "static"; | |
| 81 | ||
| 82 | pub fn init(c: *Context, parent: *Scope, labeled: bool) !Block { | |
| 83 | var blk = Block{ | |
| 84 | .base = .{ | |
| 85 | .id = .block, | |
| 86 | .parent = parent, | |
| 87 | }, | |
| 88 | .statements = std.ArrayList(Node).init(c.gpa), | |
| 89 | .variables = AliasList.init(c.gpa), | |
| 90 | .variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa), | |
| 91 | }; | |
| 92 | if (labeled) { | |
| 93 | blk.label = try blk.makeMangledName(c, "blk"); | |
| 94 | } | |
| 95 | return blk; | |
| 96 | } | |
| 97 | ||
| 98 | pub fn deinit(self: *Block) void { | |
| 99 | self.statements.deinit(); | |
| 100 | self.variables.deinit(); | |
| 101 | self.variable_discards.deinit(); | |
| 102 | self.* = undefined; | |
| 103 | } | |
| 104 | ||
| 105 | pub fn complete(self: *Block, c: *Context) !Node { | |
| 106 | if (self.base.parent.?.id == .do_loop) { | |
| 107 | // We reserve 1 extra statement if the parent is a do_loop. This is in case of | |
| 108 | // do while, we want to put `if (cond) break;` at the end. | |
| 109 | const alloc_len = self.statements.items.len + @intFromBool(self.base.parent.?.id == .do_loop); | |
| 110 | var stmts = try c.arena.alloc(Node, alloc_len); | |
| 111 | stmts.len = self.statements.items.len; | |
| 112 | @memcpy(stmts[0..self.statements.items.len], self.statements.items); | |
| 113 | return Tag.block.create(c.arena, .{ | |
| 114 | .label = self.label, | |
| 115 | .stmts = stmts, | |
| 116 | }); | |
| 117 | } | |
| 118 | if (self.statements.items.len == 0) return Tag.empty_block.init(); | |
| 119 | return Tag.block.create(c.arena, .{ | |
| 120 | .label = self.label, | |
| 121 | .stmts = try c.arena.dupe(Node, self.statements.items), | |
| 122 | }); | |
| 123 | } | |
| 124 | ||
| 125 | /// Given the desired name, return a name that does not shadow anything from outer scopes. | |
| 126 | /// Inserts the returned name into the scope. | |
| 127 | /// The name will not be visible to callers of getAlias. | |
| 128 | pub fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 { | |
| 129 | return scope.createMangledName(c, name, true); | |
| 130 | } | |
| 131 | ||
| 132 | /// Same as reserveMangledName, but enables the alias immediately. | |
| 133 | pub fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 { | |
| 134 | return scope.createMangledName(c, name, false); | |
| 135 | } | |
| 136 | ||
| 137 | pub fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 { | |
| 138 | const name_copy = try c.arena.dupe(u8, name); | |
| 139 | var proposed_name = name_copy; | |
| 140 | while (scope.contains(proposed_name)) { | |
| 141 | scope.mangle_count += 1; | |
| 142 | proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count }); | |
| 143 | } | |
| 144 | const new_mangle = try scope.variables.addOne(); | |
| 145 | if (reservation) { | |
| 146 | new_mangle.* = .{ .name = name_copy, .alias = name_copy }; | |
| 147 | } else { | |
| 148 | new_mangle.* = .{ .name = name_copy, .alias = proposed_name }; | |
| 149 | } | |
| 150 | return proposed_name; | |
| 151 | } | |
| 152 | ||
| 153 | pub fn getAlias(scope: *Block, name: []const u8) []const u8 { | |
| 154 | for (scope.variables.items) |p| { | |
| 155 | if (std.mem.eql(u8, p.name, name)) | |
| 156 | return p.alias; | |
| 157 | } | |
| 158 | return scope.base.parent.?.getAlias(name); | |
| 159 | } | |
| 160 | ||
| 161 | pub fn localContains(scope: *Block, name: []const u8) bool { | |
| 162 | for (scope.variables.items) |p| { | |
| 163 | if (std.mem.eql(u8, p.alias, name)) | |
| 164 | return true; | |
| 165 | } | |
| 166 | return false; | |
| 167 | } | |
| 168 | ||
| 169 | pub fn contains(scope: *Block, name: []const u8) bool { | |
| 170 | if (scope.localContains(name)) | |
| 171 | return true; | |
| 172 | return scope.base.parent.?.contains(name); | |
| 173 | } | |
| 174 | ||
| 175 | pub fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void { | |
| 176 | const name_node = try Tag.identifier.create(c.arena, name); | |
| 177 | const discard = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = name_node }); | |
| 178 | try scope.statements.append(discard); | |
| 179 | try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?); | |
| 180 | } | |
| 181 | }; | |
| 182 | ||
| 183 | pub const Root = struct { | |
| 184 | base: Scope, | |
| 185 | sym_table: SymbolTable, | |
| 186 | macro_table: SymbolTable, | |
| 187 | context: *Context, | |
| 188 | nodes: std.ArrayList(Node), | |
| 189 | ||
| 190 | pub fn init(c: *Context) Root { | |
| 191 | return .{ | |
| 192 | .base = .{ | |
| 193 | .id = .root, | |
| 194 | .parent = null, | |
| 195 | }, | |
| 196 | .sym_table = SymbolTable.init(c.gpa), | |
| 197 | .macro_table = SymbolTable.init(c.gpa), | |
| 198 | .context = c, | |
| 199 | .nodes = std.ArrayList(Node).init(c.gpa), | |
| 200 | }; | |
| 201 | } | |
| 202 | ||
| 203 | pub fn deinit(scope: *Root) void { | |
| 204 | scope.sym_table.deinit(); | |
| 205 | scope.macro_table.deinit(); | |
| 206 | scope.nodes.deinit(); | |
| 207 | } | |
| 208 | ||
| 209 | /// Check if the global scope contains this name, without looking into the "future", e.g. | |
| 210 | /// ignore the preprocessed decl and macro names. | |
| 211 | pub fn containsNow(scope: *Root, name: []const u8) bool { | |
| 212 | return scope.sym_table.contains(name) or scope.macro_table.contains(name); | |
| 213 | } | |
| 214 | ||
| 215 | /// Check if the global scope contains the name, includes all decls that haven't been translated yet. | |
| 216 | pub fn contains(scope: *Root, name: []const u8) bool { | |
| 217 | return scope.containsNow(name) or scope.context.global_names.contains(name) or scope.context.weak_global_names.contains(name); | |
| 218 | } | |
| 219 | }; | |
| 220 | ||
| 221 | pub fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block { | |
| 222 | var scope = inner; | |
| 223 | while (true) { | |
| 224 | switch (scope.id) { | |
| 225 | .root => unreachable, | |
| 226 | .block => return @fieldParentPtr(Block, "base", scope), | |
| 227 | .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c), | |
| 228 | else => scope = scope.parent.?, | |
| 229 | } | |
| 230 | } | |
| 231 | } | |
| 232 | ||
| 233 | pub fn findBlockReturnType(inner: *Scope) Type { | |
| 234 | var scope = inner; | |
| 235 | while (true) { | |
| 236 | switch (scope.id) { | |
| 237 | .root => unreachable, | |
| 238 | .block => { | |
| 239 | const block = @fieldParentPtr(Block, "base", scope); | |
| 240 | if (block.return_type) |ty| return ty; | |
| 241 | scope = scope.parent.?; | |
| 242 | }, | |
| 243 | else => scope = scope.parent.?, | |
| 244 | } | |
| 245 | } | |
| 246 | } | |
| 247 | ||
| 248 | pub fn getAlias(scope: *Scope, name: []const u8) []const u8 { | |
| 249 | return switch (scope.id) { | |
| 250 | .root => return name, | |
| 251 | .block => @fieldParentPtr(Block, "base", scope).getAlias(name), | |
| 252 | .loop, .do_loop, .condition => scope.parent.?.getAlias(name), | |
| 253 | }; | |
| 254 | } | |
| 255 | ||
| 256 | pub fn contains(scope: *Scope, name: []const u8) bool { | |
| 257 | return switch (scope.id) { | |
| 258 | .root => @fieldParentPtr(Root, "base", scope).contains(name), | |
| 259 | .block => @fieldParentPtr(Block, "base", scope).contains(name), | |
| 260 | .loop, .do_loop, .condition => scope.parent.?.contains(name), | |
| 261 | }; | |
| 262 | } | |
| 263 | ||
| 264 | pub fn getBreakableScope(inner: *Scope) *Scope { | |
| 265 | var scope = inner; | |
| 266 | while (true) { | |
| 267 | switch (scope.id) { | |
| 268 | .root => unreachable, | |
| 269 | .loop, .do_loop => return scope, | |
| 270 | else => scope = scope.parent.?, | |
| 271 | } | |
| 272 | } | |
| 273 | } | |
| 274 | ||
| 275 | /// Appends a node to the first block scope if inside a function, or to the root tree if not. | |
| 276 | pub fn appendNode(inner: *Scope, node: Node) !void { | |
| 277 | var scope = inner; | |
| 278 | while (true) { | |
| 279 | switch (scope.id) { | |
| 280 | .root => { | |
| 281 | const root = @fieldParentPtr(Root, "base", scope); | |
| 282 | return root.nodes.append(node); | |
| 283 | }, | |
| 284 | .block => { | |
| 285 | const block = @fieldParentPtr(Block, "base", scope); | |
| 286 | return block.statements.append(node); | |
| 287 | }, | |
| 288 | else => scope = scope.parent.?, | |
| 289 | } | |
| 290 | } | |
| 291 | } | |
| 292 | ||
| 293 | pub fn skipVariableDiscard(inner: *Scope, name: []const u8) void { | |
| 294 | var scope = inner; | |
| 295 | while (true) { | |
| 296 | switch (scope.id) { | |
| 297 | .root => return, | |
| 298 | .block => { | |
| 299 | const block = @fieldParentPtr(Block, "base", scope); | |
| 300 | if (block.variable_discards.get(name)) |discard| { | |
| 301 | discard.data.should_skip = true; | |
| 302 | return; | |
| 303 | } | |
| 304 | }, | |
| 305 | else => {}, | |
| 306 | } | |
| 307 | scope = scope.parent.?; | |
| 308 | } | |
| 309 | } | |
| 310 | }; | |
| 311 | } |