Commit ba9b9cb38d

Andrew Kelley <andrew@ziglang.org>
2021-04-30 03:25:25
AstGen: implement function prototypes with alignment exprs
1 parent 2eef83e
src/AstGen.zig
@@ -1048,10 +1048,12 @@ pub fn fnProtoExpr(
         assert(param_type_i == param_count);
     }
 
-    if (fn_proto.ast.align_expr != 0) {
-        return astgen.failNode(fn_proto.ast.align_expr, "TODO: AstGen: implement function prototypes with alignment expressions", .{});
+    const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
+        break :inst try expr(gz, scope, align_rl, fn_proto.ast.align_expr);
+    };
+    if (fn_proto.ast.section_expr != 0) {
+        return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{});
     }
-    assert(fn_proto.ast.section_expr == 0); // caught by the parser
 
     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
     const is_inferred_error = token_tags[maybe_bang] == .bang;
@@ -1081,6 +1083,7 @@ pub fn fnProtoExpr(
         .param_types = param_types,
         .body = &[0]Zir.Inst.Index{},
         .cc = cc,
+        .align_inst = align_inst,
         .lib_name = 0,
         .is_var_args = is_var_args,
         .is_inferred_error = false,
@@ -2794,6 +2797,7 @@ fn fnDecl(
             .param_types = param_types,
             .body = &[0]Zir.Inst.Index{},
             .cc = cc,
+            .align_inst = .none, // passed in the per-decl data
             .lib_name = lib_name,
             .is_var_args = is_var_args,
             .is_inferred_error = false,
@@ -2866,6 +2870,7 @@ fn fnDecl(
             .param_types = param_types,
             .body = fn_gz.instructions.items,
             .cc = cc,
+            .align_inst = .none, // passed in the per-decl data
             .lib_name = lib_name,
             .is_var_args = is_var_args,
             .is_inferred_error = is_inferred_error,
@@ -3167,6 +3172,7 @@ fn testDecl(
         .param_types = &[0]Zir.Inst.Ref{},
         .body = fn_block.instructions.items,
         .cc = .none,
+        .align_inst = .none,
         .lib_name = 0,
         .is_var_args = false,
         .is_inferred_error = true,
src/Module.zig
@@ -1372,6 +1372,7 @@ pub const Scope = struct {
             body: []const Zir.Inst.Index,
             ret_ty: Zir.Inst.Ref,
             cc: Zir.Inst.Ref,
+            align_inst: Zir.Inst.Ref,
             lib_name: u32,
             is_var_args: bool,
             is_inferred_error: bool,
@@ -1385,12 +1386,15 @@ pub const Scope = struct {
             try gz.instructions.ensureUnusedCapacity(gpa, 1);
             try astgen.instructions.ensureUnusedCapacity(gpa, 1);
 
-            if (args.cc != .none or args.lib_name != 0 or args.is_var_args or args.is_test) {
+            if (args.cc != .none or args.lib_name != 0 or
+                args.is_var_args or args.is_test or args.align_inst != .none)
+            {
                 try astgen.extra.ensureUnusedCapacity(
                     gpa,
                     @typeInfo(Zir.Inst.ExtendedFunc).Struct.fields.len +
                         args.param_types.len + args.body.len +
                         @boolToInt(args.lib_name != 0) +
+                        @boolToInt(args.align_inst != .none) +
                         @boolToInt(args.cc != .none),
                 );
                 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedFunc{
@@ -1399,11 +1403,14 @@ pub const Scope = struct {
                     .param_types_len = @intCast(u32, args.param_types.len),
                     .body_len = @intCast(u32, args.body.len),
                 });
+                if (args.lib_name != 0) {
+                    astgen.extra.appendAssumeCapacity(args.lib_name);
+                }
                 if (args.cc != .none) {
                     astgen.extra.appendAssumeCapacity(@enumToInt(args.cc));
                 }
-                if (args.lib_name != 0) {
-                    astgen.extra.appendAssumeCapacity(args.lib_name);
+                if (args.align_inst != .none) {
+                    astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst));
                 }
                 astgen.appendRefsAssumeCapacity(args.param_types);
                 astgen.extra.appendSliceAssumeCapacity(args.body);
@@ -1418,6 +1425,7 @@ pub const Scope = struct {
                             .is_inferred_error = args.is_inferred_error,
                             .has_lib_name = args.lib_name != 0,
                             .has_cc = args.cc != .none,
+                            .has_align = args.align_inst != .none,
                             .is_test = args.is_test,
                         }),
                         .operand = payload_index,
src/Sema.zig
@@ -2737,6 +2737,7 @@ fn zirFunc(
         body,
         extra.data.return_type,
         .Unspecified,
+        Value.initTag(.null_value),
         false,
         inferred_error_set,
     );
@@ -2750,6 +2751,7 @@ fn funcCommon(
     body: []const Zir.Inst.Index,
     zir_return_type: Zir.Inst.Ref,
     cc: std.builtin.CallingConvention,
+    align_val: Value,
     var_args: bool,
     inferred_error_set: bool,
 ) InnerError!*Inst {
@@ -2762,7 +2764,7 @@ fn funcCommon(
     }
 
     // Hot path for some common function types.
-    if (zir_param_types.len == 0 and !var_args) {
+    if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value) {
         if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
             return sema.mod.constType(sema.arena, src, Type.initTag(.fn_noreturn_no_args));
         }
@@ -2789,6 +2791,10 @@ fn funcCommon(
         param_types[i] = try sema.resolveType(block, src, param_type);
     }
 
+    if (align_val.tag() != .null_value) {
+        return sema.mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{});
+    }
+
     const fn_ty = try Type.Tag.function.create(sema.arena, .{
         .param_types = param_types,
         .return_type = return_type,
@@ -5432,6 +5438,7 @@ fn zirFuncExtended(
     const extra = sema.code.extraData(Zir.Inst.ExtendedFunc, extended.operand);
     const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
     const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = extra.data.src_node };
+    const align_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at align
     const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small);
 
     var extra_index: usize = extra.end;
@@ -5455,6 +5462,13 @@ fn zirFuncExtended(
         break :blk cc;
     } else .Unspecified;
 
+    const align_val: Value = if (small.has_align) blk: {
+        const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+        extra_index += 1;
+        const align_tv = try sema.resolveInstConst(block, align_src, align_ref);
+        break :blk align_tv.val;
+    } else Value.initTag(.null_value);
+
     const param_types = sema.code.refSlice(extra_index, extra.data.param_types_len);
     extra_index += param_types.len;
 
@@ -5467,6 +5481,7 @@ fn zirFuncExtended(
         body,
         extra.data.return_type,
         cc,
+        align_val,
         small.is_var_args,
         small.is_inferred_error,
     );
src/Zir.zig
@@ -2189,8 +2189,9 @@ pub const Inst = struct {
     /// Trailing:
     /// 0. lib_name: u32, // null terminated string index, if has_lib_name is set
     /// 1. cc: Ref, // if has_cc is set
-    /// 2. param_type: Ref // for each param_types_len
-    /// 3. body: Index // for each body_len
+    /// 2. align: Ref, // if has_align is set
+    /// 3. param_type: Ref // for each param_types_len
+    /// 4. body: Index // for each body_len
     pub const ExtendedFunc = struct {
         src_node: i32,
         return_type: Ref,
@@ -2202,8 +2203,9 @@ pub const Inst = struct {
             is_inferred_error: bool,
             has_lib_name: bool,
             has_cc: bool,
+            has_align: bool,
             is_test: bool,
-            _: u11 = undefined,
+            _: u10 = undefined,
         };
     };
 
@@ -3966,6 +3968,7 @@ const Writer = struct {
             inferred_error_set,
             false,
             .none,
+            .none,
             body,
             src,
         );
@@ -3988,6 +3991,11 @@ const Writer = struct {
             extra_index += 1;
             break :blk cc;
         };
+        const align_inst: Inst.Ref = if (!small.has_align) .none else blk: {
+            const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
+            extra_index += 1;
+            break :blk align_inst;
+        };
 
         const param_types = self.code.refSlice(extra_index, extra.data.param_types_len);
         extra_index += param_types.len;
@@ -4001,6 +4009,7 @@ const Writer = struct {
             small.is_inferred_error,
             small.is_var_args,
             cc,
+            align_inst,
             body,
             src,
         );
@@ -4053,6 +4062,7 @@ const Writer = struct {
         inferred_error_set: bool,
         var_args: bool,
         cc: Inst.Ref,
+        align_inst: Inst.Ref,
         body: []const Inst.Index,
         src: LazySrcLoc,
     ) !void {
@@ -4064,6 +4074,7 @@ const Writer = struct {
         try stream.writeAll("], ");
         try self.writeInstRef(stream, ret_ty);
         try self.writeOptionalInstRef(stream, ", cc=", cc);
+        try self.writeOptionalInstRef(stream, ", align=", align_inst);
         try self.writeFlag(stream, ", vargs", var_args);
         try self.writeFlag(stream, ", inferror", inferred_error_set);
 
BRANCH_TODO
@@ -1,3 +1,4 @@
+ * decouple AstGen from Module, Compilation
  * modify dbg_stmt ZIR instructions to have line/column rather than node indexes
  * AstGen threadlocal
  * extern "foo" for vars and for functions