Thread overview
Reducing source code: weak+alias values in array
Apr 27, 2015
Jens Bauer
Apr 29, 2015
Artur Skawina
Apr 29, 2015
Jens Bauer
May 01, 2015
Jens Bauer
May 01, 2015
Artur Skawina
May 02, 2015
Jens Bauer
May 02, 2015
Jens Bauer
May 02, 2015
Jens Bauer
May 02, 2015
Artur Skawina
May 02, 2015
Jens Bauer
April 27, 2015
I was wondering if there's a way to reduce my bulky startup files a bit.

If using the GNU Assembler (GAS), then one can reduce the code using a macro like this:


/* The EXC macro makes a weak+alias for the
 * symbol 'value', then it stores the value in memory: */
	.macro		EXC	value,defaultValue
	.ifnb		\defaultValue
	.weakref	\value,\defaultValue
	.else
	.weakref	\value,defaultExceptionVector
	.endif
	.4byte		\value
	.endm


/* The exception vector now looks quite simple: */
isr_vector:
	.4byte		_stack
	EXC		Reset_Handler,defaultResetHandler
	EXC		NMI_Handler
	EXC		HardFault_Handler
	EXC		MemManage_Handler
	EXC		BusFault_Handler
	EXC		UsageFault_Handler
	.4byte		0
	.4byte		0
	.4byte		0
	.4byte		0
	EXC		SVC_Handler
	EXC		DebugMon_Handler
	.4byte		0
	EXC		PendSV_Handler
	EXC		SysTick_Handler

An example on one of my bulky startup files:
https://github.com/jens-gpio/MCU/blob/master/startup/stm/stm32f439_startup.d

I was thinking about alias (Tuple) or similar, but I'm not sure it would work if not declaring the label weak+alias in advance. Any thoughts ?
(Other suggestions on how to reduce the source code size are also welcome).
April 29, 2015
On 04/27/15 19:49, Jens Bauer via Digitalmars-d-learn wrote:
> I was wondering if there's a way to reduce my bulky startup files a bit.
> 
> If using the GNU Assembler (GAS), then one can reduce the code using a macro like this:
> 
> 
> /* The EXC macro makes a weak+alias for the
>  * symbol 'value', then it stores the value in memory: */
>     .macro        EXC    value,defaultValue
>     .ifnb        \defaultValue
>     .weakref    \value,\defaultValue
>     .else
>     .weakref    \value,defaultExceptionVector
>     .endif
>     .4byte        \value
>     .endm
> 
> 
> /* The exception vector now looks quite simple: */
> isr_vector:
>     .4byte        _stack
>     EXC        Reset_Handler,defaultResetHandler
>     EXC        NMI_Handler
>     EXC        HardFault_Handler
>     EXC        MemManage_Handler
>     EXC        BusFault_Handler
>     EXC        UsageFault_Handler
>     .4byte        0
>     .4byte        0
>     .4byte        0
>     .4byte        0
>     EXC        SVC_Handler
>     EXC        DebugMon_Handler
>     .4byte        0
>     EXC        PendSV_Handler
>     EXC        SysTick_Handler
> 
> An example on one of my bulky startup files: https://github.com/jens-gpio/MCU/blob/master/startup/stm/stm32f439_startup.d

Just create a helper module, which the startup files can all use to generate the data from a dsl. Eg

   import my.helper.mod;

   mixin(VectorFuncs!(q{
      PTR stack = {`_stack`};
      EXC Reset_Handler = {`defaultResetHandler`};
      EXC NMI_Handler;
      EXC HardFault_Handler;
      PAD pad01;
      PAD pad02;
      //...
   }));


// Then, in my.helper.mod:

   @property /*@section("discard.etc")*/ VectorFuncs(string dsl)() {
      static struct A {
         struct PTR { string n; @property s() {return `cast(VectorFunc)&`~n;} }
         struct PAD { string n; @property s() {return `cast(VectorFunc)null`;} }
         struct EXC { string n = `defaultExceptionHandler`; @property s() {return null;} }
         mixin(dsl);
      }
      string code;

      foreach (I, M; A.init.tupleof)
         static if (is(typeof(M)==A.EXC))
            code ~= `@weakalias("`~M.n~`") extern (C) void ` ~ __traits(identifier, A.tupleof[I]) ~ "();\n";

      code ~= "\n@isr_vector VectorFunc[] g_pfnVectors = [\n";
      foreach (I, M; A.init.tupleof)
            code ~= "   " ~ (M.s?M.s:"&"~__traits(identifier, A.tupleof[I])) ~ ",\n";
      code ~= "];\n";

      return code;
   }

and what the compiler will see when building the startup modules will look like

   @weakalias("defaultResetHandler") extern (C) void Reset_Handler();
   @weakalias("defaultExceptionHandler") extern (C) void NMI_Handler();
   @weakalias("defaultExceptionHandler") extern (C) void HardFault_Handler();

   @isr_vector VectorFunc[] g_pfnVectors = [
      cast(VectorFunc)&_stack,
      &Reset_Handler,
      &NMI_Handler,
      &HardFault_Handler,
      cast(VectorFunc)null,
      cast(VectorFunc)null,
   ];

artur
April 29, 2015
On Wednesday, 29 April 2015 at 13:58:14 UTC, Artur Skawina wrote:
> On 04/27/15 19:49, Jens Bauer via Digitalmars-d-learn wrote:
>> I was wondering if there's a way to reduce my bulky startup files a bit.
>> 
>> If using the GNU Assembler (GAS), then one can reduce the code using a macro like this:
>> 
{snip}
>> 
>> An example on one of my bulky startup files:
>> https://github.com/jens-gpio/MCU/blob/master/startup/stm/stm32f439_startup.d
>
> Just create a helper module, which the startup files can all
> use to generate the data from a dsl. Eg

That is very cool and quite short. Thank you!
I'll try it out ... I'm already starting to understand some of what's going on, but it'll probably be clearer when I've typed the code (I do not just do copy-and-paste, as I've found out that I learn better by forcing each letter through my brain).
May 01, 2015
On Wednesday, 29 April 2015 at 13:58:14 UTC, Artur Skawina wrote:
> On 04/27/15 19:49, Jens Bauer via Digitalmars-d-learn wrote:
>> I was wondering if there's a way to reduce my bulky startup files a bit.
{snip}

> Just create a helper module, which the startup files can all
> use to generate the data from a dsl. Eg
{snip}

I've experimented a little with the code, but ran into two minor problems.

        code ~= `@weakalias("`~M.n~`") extern (C) void ` ~ __traits(identifier, A.tupleof[I]) ~ "();\n";

The above part gives me some problems; I do not know how to create the @weakalias.
I can make a @exc (which defaults to defaultExceptionVector) and @rst (which defaults to defaultResetHandler), but I have not yet succeeded in making a universal @weakalias.

alias Tuple(A...) = A;
alias rst = Tuple!(weak, gcc.attribute.attribute("alias", "defaultResetHandler"));
alias exc = Tuple!(weak, gcc.attribute.attribute("alias", "defaultExceptionHandler"));

... I tried messing with changing the above code, but no luck so far:

    code ~= `@weak @gcc.attribute.attribute("alias", "`~M.n~`") extern (C) void ` ~ __traits(identifier, A.tupleof[I]) ~ "();\n";


If I modify the above to ...
	foreach (I, M; A.init.tupleof)
	{
		static if (is(typeof(M)==A.RST))
			code ~= `@rst extern (C) void ` ~ __traits(identifier, A.tupleof[I]) ~ "();\n";
		static if (is(typeof(M)==A.EXC))
			code ~= `@exc extern (C) void ` ~ __traits(identifier, A.tupleof[I]) ~ "();\n";
	}
... then it will build and produce the expected results. That requires replacing the EXC for the ResetHandler by RST, I like the original code much better, though. :)


I also had some trouble with the exception vectors not being generated, and it turned out that my array was dynamic instead of static.

For now, I've just made the array a constant size (100 elements); eg. changed ...
    code ~= "\n@isr_vector VectorFunc[] g_pfnVectors = [\n";
... to ...
    code ~= "\n@isr_vector VectorFunc[100] g_pfnVectors = [\n";
... Is it possible to generate a static array without specifying a fixed array size ?

Apart from the above two mentioned problems, the code builds and produces the expected results. I even started to understand some parts of it, and I find it pretty awesome. ;)
May 01, 2015
On 05/01/15 22:29, Jens Bauer via Digitalmars-d-learn wrote:
> On Wednesday, 29 April 2015 at 13:58:14 UTC, Artur Skawina wrote:
>> On 04/27/15 19:49, Jens Bauer via Digitalmars-d-learn wrote:
>>> I was wondering if there's a way to reduce my bulky startup files a bit.
> {snip}
> 
>> Just create a helper module, which the startup files can all use to generate the data from a dsl. Eg
> {snip}
> 
> I've experimented a little with the code, but ran into two minor problems.
> 
>         code ~= `@weakalias("`~M.n~`") extern (C) void ` ~ __traits(identifier, A.tupleof[I]) ~ "();\n";
> 
> The above part gives me some problems; I do not know how to create the @weakalias.

My fault; I only looked at the generated code, but never actually tested it.

Use `@weakalias!"blah"` instead:

   enum weakalias(string A) = gcc.attribute.attribute("alias", A);

   @weakalias!"defaultResetHandler" extern (C) void Reset_Handler();



> I also had some trouble with the exception vectors not being generated, and it turned out that my array was dynamic instead of static.
> 
> For now, I've just made the array a constant size (100 elements); eg. changed ...
>     code ~= "\n@isr_vector VectorFunc[] g_pfnVectors = [\n";
> ... to ...
>     code ~= "\n@isr_vector VectorFunc[100] g_pfnVectors = [\n";
> ... Is it possible to generate a static array without specifying a fixed array size ?

No, but you can just do:

   code ~= "\n@isr_vector VectorFunc[" ~ A.tupleof.length.stringof ~ "] g_pfnVectors = [\n";

> Apart from the above two mentioned problems, the code builds and produces the expected results. I even started to understand some parts of it, and I find it pretty awesome. ;)

(Ab)using the compiler for the DSL parsing gets really awesome once you
use other D features like multiple named member initializers, lambdas and/or
static-ifs etc /inside/ the DSL. Next thing you know you'll be using DSLs
that generate other DSLs that emit plain D code, with a few layers of
expression templates in between... :)

artur
May 02, 2015
On Friday, 1 May 2015 at 21:36:29 UTC, Artur Skawina wrote:
> On 05/01/15 22:29, Jens Bauer via Digitalmars-d-learn wrote:
>> On Wednesday, 29 April 2015 at 13:58:14 UTC, Artur Skawina wrote:
> Use `@weakalias!"blah"` instead:
>
>    enum weakalias(string A) = gcc.attribute.attribute("alias", A);
>
>    @weakalias!"defaultResetHandler" extern (C) void Reset_Handler();

Here's my resulting code-snippet:

enum weak = gcc.attribute.attribute("weak");

alias Tuple(A...) = A;
alias weakalias(string A) = Tuple!(weak, gcc.attribute.attribute("alias", A));

...

    foreach (I, M; A.init.tupleof)
    {
        static if (is(typeof(M)==A.EXC))
            code ~= `@weakalias!"`~M.n~`" extern (C) void ` ~ __traits(identifier, A.tupleof[I]) ~ "();\n";
    }

... because the 'alias' attribute does not automcatically include the 'weak' attribute.
It seems to work, but did I write the code correctly ?

>> ... Is it possible to generate a static array without specifying a fixed array size ?
>
> No, but you can just do:
>
>    code ~= "\n@isr_vector VectorFunc[" ~ A.tupleof.length.stringof ~ "] g_pfnVectors = [\n";

That works great. Thank you for your valuable help. :)

>> Apart from the above two mentioned problems, the code builds and produces the expected results. I even started to understand some parts of it, and I find it pretty awesome. ;)
>
> (Ab)using the compiler for the DSL parsing gets really awesome {snip}

I remember the Atari 130XE (and thus the Atari 600XL/800XL) were able to auto-generate basic-code, saving a lot of typing. Though different, this really reminds me of those days. :)

I've never touched a C++ template, but I've been using #define in C.
Though #define is a neat feature, it does not beat this, and as I've never had enough reason to use C++ templates, I expect they're not as powerful as D's ability to generate code at compile-time.
May 02, 2015
> On 04/27/15 19:49, Jens Bauer via Digitalmars-d-learn wrote:
>> I was wondering if there's a way to reduce my bulky startup files a bit.
>> 
On Wednesday, 29 April 2015 at 13:58:14 UTC, Artur Skawina wrote:
>    mixin(VectorFuncs!(q{
>       PTR stack = {`_stack`};
>       EXC Reset_Handler = {`defaultResetHandler`};
>       EXC NMI_Handler;
>       EXC HardFault_Handler;
>       PAD pad01;
>       PAD pad02;
>       //...
>    }));

For some reason, my build time has increased dramatically...

Building with 1 vector takes 0.6 seconds.
Building with 2 vector takes 0.7 seconds.
Building with 4 vector takes 0.9 seconds.
Building with 8 vector takes 1.1 seconds.
Building with 16 vectors takes 1.7 seconds.
Building with 32 vectors takes 3.4 seconds.
Building with 64 vectors takes 12.4 seconds.
Building with 112 vectors takes 55.5 seconds.
Building with 113 vectors takes 56.7 seconds.

... Two foreach loops shouldn't take that long, right ?
The generated code appears to be correct, though.
May 02, 2015
On Saturday, 2 May 2015 at 03:21:38 UTC, Jens Bauer wrote:
> For some reason, my build time has increased dramatically...
>
> Building with 1 vector takes 0.6 seconds.
> Building with 2 vector takes 0.7 seconds.
> Building with 4 vector takes 0.9 seconds.
> Building with 8 vector takes 1.1 seconds.
> Building with 16 vectors takes 1.7 seconds.
> Building with 32 vectors takes 3.4 seconds.
> Building with 64 vectors takes 12.4 seconds.
> Building with 112 vectors takes 55.5 seconds.
> Building with 113 vectors takes 56.7 seconds.

Here's the source code for the file I'm building:

http://pastebin.com/pCh9e7hQ
May 02, 2015
On 05/02/15 05:28, Jens Bauer via Digitalmars-d-learn wrote:
> On Saturday, 2 May 2015 at 03:21:38 UTC, Jens Bauer wrote:
>> For some reason, my build time has increased dramatically...
>>
>> Building with 1 vector takes 0.6 seconds.
>> Building with 2 vector takes 0.7 seconds.
>> Building with 4 vector takes 0.9 seconds.
>> Building with 8 vector takes 1.1 seconds.
>> Building with 16 vectors takes 1.7 seconds.
>> Building with 32 vectors takes 3.4 seconds.
>> Building with 64 vectors takes 12.4 seconds.
>> Building with 112 vectors takes 55.5 seconds.
>> Building with 113 vectors takes 56.7 seconds.

Apparently CTFE can be very inefficient sometimes -- compiler issue. Can't think of a workaround right now; manually parsing (instead of using mixins) might help, but that would make the solution less obvious...

> Here's the source code for the file I'm building:
> 
> http://pastebin.com/pCh9e7hQ

For some reason I was never really affected by the horrible CTFE perf. For example, your code from that link, after a few tweaks to get it to build, compiles in ~3s for me. (64 bit x86 linux gdc build)

artur
May 02, 2015
On Saturday, 2 May 2015 at 13:08:27 UTC, Artur Skawina wrote:
> On 05/02/15 05:28, Jens Bauer via Digitalmars-d-learn wrote:
>> On Saturday, 2 May 2015 at 03:21:38 UTC, Jens Bauer wrote:
>>> For some reason, my build time has increased dramatically...
>>>
>>> Building with 1 vector takes 0.6 seconds.
>>> Building with 2 vector takes 0.7 seconds.
>>> Building with 4 vector takes 0.9 seconds.
>>> Building with 8 vector takes 1.1 seconds.
>>> Building with 16 vectors takes 1.7 seconds.
>>> Building with 32 vectors takes 3.4 seconds.
>>> Building with 64 vectors takes 12.4 seconds.
>>> Building with 112 vectors takes 55.5 seconds.
>>> Building with 113 vectors takes 56.7 seconds.
>
> Apparently CTFE can be very inefficient sometimes -- compiler
> issue. Can't think of a workaround right now; manually parsing
> (instead of using mixins) might help, but that would make the
> solution less obvious...

I'll try and make a few experiments to see if there's something that helps speeding it up.

>> http://pastebin.com/pCh9e7hQ
>
> For some reason I was never really affected by the horrible
> CTFE perf. For example, your code from that link, after a few
> tweaks to get it to build, compiles in ~3s for me. (64 bit x86
> linux gdc build)

That's quick. I'd expect your computer to be a bit faster than mine. ;)
I have a QuadCore 2.5GHz PowerMac G5. But I'll also be building on a Dual 2GHz ARM Cortex-A7 based CubieBoard2 if I succeed building a D compiler for it; this. I think it's important for the user that the compilation time is kept low, because many people will be building on Cortex-A based devices.