Last active 9 months ago


Last active 2 years ago


Last active 3 years ago


Last active 3 years ago


Last active 4 years ago


Last active 4 years ago


Last active 4 years ago


Last active 4 years ago

#83 Stage2 fails to compile 1 year, 1 month ago

Comment by ~mcf on ~mcf/cproc

The offending commit is the introduction of C23 keywords. There is a function called constexpr(), which needs to be renamed since it conflicts with the C23 keyword. I need to pick a new name I like.

#80 segfault when returning before a switch statement 1 year, 3 months ago

Comment by ~mcf on ~mcf/cproc

This is a long-standing issue that stems from funcinst returning NULL when we haven't introduced a new QBE block, and we have already have a jump for the current block. The intent was to avoid generating unreachable code, but it causes problems when more control flow follows after the jump.

One fix could be to make stmt() split up parsing and code generation, and skip the latter if the current block is already terminated. However, the much simpler solution is just to insert a dummy block when we encounter this situation. QBE will discard unreachable blocks quite early in the pipeline, so I think that's what I'll do.

Fixed by 4fa48e716d6381e51ae8dc7a9992c4c7d50219a1.


#79 Assertion failed: tq == QUALNONE (expr.c: decay: 101) 1 year, 3 months ago

Comment by ~mcf on ~mcf/cproc

I noticed this issue also during bootstrap on OpenBSD (https://builds.sr.ht/~mcf/job/757621).

The issue is related to qualifiers on array types. The C standard says that when an array type is qualified, the qualifiers apply to the element type, not the array type itself. Although this is handled partially at the end of decl.c:declspecs(), there are still a few cases where we incorrectly applied qualifiers to an array type:

  1. If you qualify a nested array type, e.g. typedef int T[1][1]; const T x;. declspecs only passes the qualifiers through one layer of array, so x ends up as "array of const array of int" instead of "array of array of const int".
  2. If you access an array member of a struct or union, the qualifiers of the struct/union get applied to the member type, but don't get passed through to the element type of the array member.

Commit b82a231 fixed a bug where an expression with array type would decay into a pointer with the qualifiers of the array type (which should always be empty as per above) and ignore the qualifiers of the array element type. However, due to 1 and 2 above, there are cases where we might end up with a qualified array type, which now trigger the assertion. I think you are hitting case 2 above (removing the const from repl_cmds is a workaround).

As for a solution, I have two ideas:

  1. Add a function that applies qualifiers to an array type by walking down until it finds a non-array type, rebuilding it as it goes and applying the qualifiers to that non-array type. Then change declspecs and postfixexpr to use that.
  2. Previously, things mostly ended up working despite this bug since the qualifiers were applied down to the element type during the implicit conversion of an array to a pointer to first element. Perhaps it would be sufficient to combine the qualifiers in decay() (i.e. t->qual | tq), and then somehow relax typecompatible() to treat "qualified array of type" and "array of qualified type" the as equivalent.

I still need to think more about it to determine the best way forward. I have some plans to change up how types are represented, which I think should make this easier to deal with. So even though it's still not quite correct, perhaps the best thing to do for now is the diff below.

diff --git a/expr.c b/expr.c
index 67976b8..532ad60 100644
--- a/expr.c
+++ b/expr.c
@@ -98,9 +98,14 @@ decay(struct expr *e)
 	tq = e->qual;
 	switch (t->kind) {
+		/*
+		TODO: qualifiers should be applied to the element
+		type of array types, not the array type itself
 		assert(tq == QUALNONE);
+		*/
 		e = mkunaryexpr(TBAND, e);
-		e->type = mkpointertype(t->base, t->qual);
+		e->type = mkpointertype(t->base, t->qual | tq);
 		e->decayed = true;
 	case TYPEFUNC:

#1 Variable-length arrays 1 year, 3 months ago

Comment by ~mcf on ~mcf/cproc

See the earlier discussion. The problem is that alloca and VLAs behave differently in a loop. Memory allocated by alloca gets deallocated when the function returns, but VLAs get deallocated at the end of the block. Naively compiling VLAs as alloca could result in stack overflows in some of the examples above.

#35 Prefixed string literals 1 year, 11 months ago

on ~mcf/cproc


#62 Build musl libc 2 years ago

Comment by ~mcf on ~mcf/cproc

It only uses -fPIC when it detects that the compiler enables it by default. Since cproc is using gcc as a preprocessor, if gcc is built with --enable-default-pie it defines __PIC__ by default, which is not correct since qbe doesn't generate position-independent code.

I pushed a couple of commits to fix this. Now, you should be able to do CC=cproc ./configure --target=x86_64-linux-musl && make -k lib/libc.a to see all the errors.

There are only a handful of unique errors, but these are all major features that need support from QBE (apart from _Complex maybe):

$ make -k lib/libc.a 2>&1 | grep -e error: -e cproc-qbe: | sort -u
./arch/x86_64/atomic_arch.h:3:2: error: inline assembly is not yet supported
./arch/x86_64/syscall_arch.h:6:2: error: inline assembly is not yet supported
./include/complex.h:16:8: error: _Complex is not yet supported
cproc-qbe: long double is not yet supported
src/network/if_nameindex.c:95:52: error: VLAs are not yet supported
src/process/execl.c:12:20: error: VLAs are not yet supported
src/process/execle.c:12:20: error: VLAs are not yet supported
src/process/execlp.c:12:20: error: VLAs are not yet supported
src/process/execvp.c:29:15: error: VLAs are not yet supported
src/search/lsearch.c:6:17: error: VLAs are not yet supported
src/string/explicit_bzero.c:6:2: error: inline assembly is not yet supported

#75 Explicit Types Confuse Initializer 2 years ago

Comment by ~mcf on ~mcf/cproc

Thanks for the clear report and test case.

This syntax is perfectly fine for initializing local variables inside a function, but not for static or global variables. The C standard describes this restriction in 6.7.9p4:

All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.

What you have here on the right hand side is a compound literal expression, which is not a constant expression or string literal. A constant expression must be one of the following (6.6p7):

  • an arithmetic constant expression,
  • a null pointer constant,
  • an address constant, or
  • an address constant for a complete object type plus or minus an integer constant expression.

I believe the reason why this works in gcc and clang is because of 6.7p10:

An implementation may accept other forms of constant expressions.

But this is not something you can rely upon in your code. And indeed, if you enable -Wpedantic, gcc will warn about this:

test.c:2:20: warning: initializer element is not constant [-Wpedantic]
    2 | struct dummy str = (struct dummy){.label = "Redraw"};

The reason that struct dummy str = {.label = "Redraw"}; works is because there is only one expression in the initializer, the string literal "Redraw", which is explicitly allowed in initializers of global and static variables.

I got in the habit of doing this back when initializer "type inference" was worse and noticed it while trying to compile a program of mine.

I'm not sure what you mean by this. C doesn't have any initializer type inference; you always have to specify the types of the objects you initialize.

#66 Handle #line directives 2 years ago

on ~mcf/cproc


#74 Segfaults on invalid syntax 2 years ago

Comment by ~mcf on ~mcf/cproc

Thanks for the report. The problem was that declarator() was allowing an abstract function declarator (struct x(), i.e. function with unspecified arguments returning struct x), even when allowabstract was false.

Fixed by 87553780.


#458 qbe fails to optimize away stack slot for unused value 2 years ago

~sircmpwn assigned ~mcf to #458 on ~sircmpwn/hare