build: Avoid linker magic in compile_time_request.c unique id generation

Avoid generating unique ids via memory locations and linker scripts.
Instead, generate them using code produced by buildcommands.py.
Utilize gcc's ability to perform static string comparisons at compile
time to produce a unique id for each unique string.

This fixes a build failure on ARM introduced in 142b92b8.  It also
reduces the complexity of the build.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2017-05-25 16:54:31 -04:00
parent f91a49c65d
commit b9940f0e0d
6 changed files with 120 additions and 73 deletions

View File

@ -75,21 +75,24 @@ $(OUT)board-link: $(KCONFIG_CONFIG)
$(Q)mkdir -p $(OUT)board-generic $(Q)mkdir -p $(OUT)board-generic
$(Q)ln -Tsf $(PWD)/src/generic $(OUT)board-generic/board $(Q)ln -Tsf $(PWD)/src/generic $(OUT)board-generic/board
$(OUT)%.o.ctr: $(OUT)%.o
$(Q)$(OBJCOPY) -j '.compile_time_request' -O binary $^ $@
$(OUT)compile_time_request.o: $(patsubst %.c, $(OUT)src/%.o.ctr,$(src-y)) ./scripts/buildcommands.py
@echo " Building $@"
$(Q)cat $(patsubst %.c, $(OUT)src/%.o.ctr,$(src-y)) > $(OUT)klipper.compile_time_request
$(Q)$(PYTHON) ./scripts/buildcommands.py -d $(OUT)klipper.dict $(OUT)klipper.compile_time_request $(OUT)compile_time_request.c
$(Q)$(CC) $(CFLAGS) -c $(OUT)compile_time_request.c -o $@
$(OUT)declfunc.lds: src/declfunc.lds.S $(OUT)declfunc.lds: src/declfunc.lds.S
@echo " Precompiling $@" @echo " Precompiling $@"
$(Q)$(CPP) $(CPPFLAGS) -D__ASSEMBLY__ $< -o $@ $(Q)$(CPP) $(CPPFLAGS) -D__ASSEMBLY__ $< -o $@
$(OUT)klipper.o: $(patsubst %.c, $(OUT)src/%.o,$(src-y)) $(OUT)declfunc.lds $(OUT)klipper.o: $(patsubst %.c, $(OUT)src/%.o,$(src-y)) $(OUT)compile_time_request.o $(OUT)declfunc.lds
@echo " Linking $@" @echo " Linking $@"
$(Q)$(CC) $(CFLAGS_klipper.o) -Wl,-T,$(OUT)declfunc.lds $(patsubst %.c, $(OUT)src/%.o,$(src-y)) -o $@ $(Q)$(CC) $(CFLAGS_klipper.o) -Wl,-T,$(OUT)declfunc.lds $(patsubst %.c, $(OUT)src/%.o,$(src-y)) $(OUT)compile_time_request.o -o $@
$(OUT)compile_time_request.o: $(OUT)klipper.o ./scripts/buildcommands.py $(OUT)klipper.elf: $(OUT)klipper.o
@echo " Building $@"
$(Q)$(OBJCOPY) -j '.compile_time_request' -O binary $< $(OUT)klipper.o.compile_time_request
$(Q)$(PYTHON) ./scripts/buildcommands.py -d $(OUT)klipper.dict $(OUT)klipper.o.compile_time_request $(OUT)compile_time_request.c
$(Q)$(CC) $(CFLAGS) -c $(OUT)compile_time_request.c -o $@
$(OUT)klipper.elf: $(OUT)klipper.o $(OUT)compile_time_request.o
@echo " Linking $@" @echo " Linking $@"
$(Q)$(CC) $(CFLAGS_klipper.elf) $^ -o $@ $(Q)$(CC) $(CFLAGS_klipper.elf) $^ -o $@

View File

@ -58,22 +58,66 @@ def build_parser(parser, iscmd, all_param_types):
out += " .max_size=%d," % (max_size,) out += " .max_size=%d," % (max_size,)
return out return out
def build_parsers(parsers, msg_to_id, all_param_types): def build_encoders(encoders, msg_to_id, all_param_types):
pcode = [] encoder_defs = []
for msgname, msg in parsers: output_code = []
encoder_code = []
did_output = {}
for msgname, msg in encoders:
msgid = msg_to_id[msg] msgid = msg_to_id[msg]
if msgid in did_output:
continue
s = msg
if s == '#empty':
s = ''
did_output[msgid] = True
code = (' if (__builtin_strcmp(str, "%s") == 0)\n'
' return &command_encoder_%s;\n' % (s, msgid))
if msgname is None: if msgname is None:
parser = msgproto.OutputFormat(msgid, msg) parser = msgproto.OutputFormat(msgid, msg)
output_code.append(code)
else: else:
parser = msgproto.MessageFormat(msgid, msg) parser = msgproto.MessageFormat(msgid, msg)
encoder_code.append(code)
parsercode = build_parser(parser, 0, all_param_types) parsercode = build_parser(parser, 0, all_param_types)
pcode.append("{%s\n}, " % (parsercode,)) encoder_defs.append(
"const struct command_encoder command_encoder_%s PROGMEM = {"
" %s\n};\n" % (
msgid, parsercode))
fmt = """ fmt = """
const struct command_encoder command_encoders[] PROGMEM __visible = {
%s %s
};
const __always_inline struct command_encoder *
ctr_lookup_encoder(const char *str)
{
%s
return NULL;
}
const __always_inline struct command_encoder *
ctr_lookup_output(const char *str)
{
%s
return NULL;
}
""" """
return fmt % ("".join(pcode).strip(),) return fmt % ("".join(encoder_defs).strip(), "".join(encoder_code).strip(),
"".join(output_code).strip())
def build_static_strings(static_strings):
code = []
for i, s in enumerate(static_strings):
code.append(' if (__builtin_strcmp(str, "%s") == 0)\n'
' return %d;\n' % (s, i))
fmt = """
uint8_t __always_inline
ctr_lookup_static_string(const char *str)
{
%s
return 0xff;
}
"""
return fmt % ("".join(code).strip(),)
def build_param_types(all_param_types): def build_param_types(all_param_types):
sorted_param_types = sorted([(i, a) for a, i in all_param_types.items()]) sorted_param_types = sorted([(i, a) for a, i in all_param_types.items()])
@ -102,7 +146,7 @@ def build_commands(cmd_by_id, messages_by_name, all_param_types):
index.append(" &%s," % (parsername,)) index.append(" &%s," % (parsername,))
parser = msgproto.MessageFormat(msgid, msg) parser = msgproto.MessageFormat(msgid, msg)
parsercode = build_parser(parser, 1, all_param_types) parsercode = build_parser(parser, 1, all_param_types)
parsers.append("const struct command_parser %s PROGMEM __visible = {" parsers.append("const struct command_parser %s PROGMEM = {"
" %s\n .flags=%s,\n .func=%s\n};" % ( " %s\n .flags=%s,\n .func=%s\n};" % (
parsername, parsercode, flags, funcname)) parsername, parsercode, flags, funcname))
index = "\n".join(index) index = "\n".join(index)
@ -113,11 +157,11 @@ def build_commands(cmd_by_id, messages_by_name, all_param_types):
%s %s
const struct command_parser * const command_index[] PROGMEM __visible = { const struct command_parser * const command_index[] PROGMEM = {
%s %s
}; };
const uint8_t command_index_size PROGMEM __visible = ARRAY_SIZE(command_index); const uint8_t command_index_size PROGMEM = ARRAY_SIZE(command_index);
""" """
return fmt % (externs, '\n'.join(parsers), index) return fmt % (externs, '\n'.join(parsers), index)
@ -147,11 +191,11 @@ def build_identify(cmd_by_id, msg_to_id, responses, static_strings
out.append('\n ') out.append('\n ')
out.append(" 0x%02x," % (ord(zdata[i]),)) out.append(" 0x%02x," % (ord(zdata[i]),))
fmt = """ fmt = """
const uint8_t command_identify_data[] PROGMEM __visible = {%s const uint8_t command_identify_data[] PROGMEM = {%s
}; };
// Identify size = %d (%d uncompressed) // Identify size = %d (%d uncompressed)
const uint32_t command_identify_size PROGMEM __visible const uint32_t command_identify_size PROGMEM
= ARRAY_SIZE(command_identify_data); = ARRAY_SIZE(command_identify_data);
""" """
return data, fmt % (''.join(out), len(zdata), len(data)) return data, fmt % (''.join(out), len(zdata), len(data))
@ -224,7 +268,7 @@ def main():
commands = {} commands = {}
messages_by_name = dict((m.split()[0], m) messages_by_name = dict((m.split()[0], m)
for m in msgproto.DefaultMessages.values()) for m in msgproto.DefaultMessages.values())
parsers = [] encoders = []
static_strings = [] static_strings = []
constants = {} constants = {}
# Parse request file # Parse request file
@ -248,7 +292,7 @@ def main():
if m is not None and m != msg: if m is not None and m != msg:
error("Conflicting definition for command '%s'" % msgname) error("Conflicting definition for command '%s'" % msgname)
messages_by_name[msgname] = msg messages_by_name[msgname] = msg
elif cmd == '_DECL_PARSER': elif cmd == '_DECL_ENCODER':
if len(parts) == 1: if len(parts) == 1:
msgname = msg = "#empty" msgname = msg = "#empty"
else: else:
@ -257,9 +301,9 @@ def main():
if m is not None and m != msg: if m is not None and m != msg:
error("Conflicting definition for message '%s'" % msgname) error("Conflicting definition for message '%s'" % msgname)
messages_by_name[msgname] = msg messages_by_name[msgname] = msg
parsers.append((msgname, msg)) encoders.append((msgname, msg))
elif cmd == '_DECL_OUTPUT': elif cmd == '_DECL_OUTPUT':
parsers.append((None, msg)) encoders.append((None, msg))
elif cmd == '_DECL_STATIC_STR': elif cmd == '_DECL_STATIC_STR':
static_strings.append(req[17:]) static_strings.append(req[17:])
elif cmd == '_DECL_CONSTANT': elif cmd == '_DECL_CONSTANT':
@ -275,14 +319,15 @@ def main():
# Create unique ids for each message type # Create unique ids for each message type
msgid = max(msgproto.DefaultMessages.keys()) msgid = max(msgproto.DefaultMessages.keys())
msg_to_id = dict((m, i) for i, m in msgproto.DefaultMessages.items()) msg_to_id = dict((m, i) for i, m in msgproto.DefaultMessages.items())
for msgname in commands.keys() + [m for n, m in parsers]: for msgname in commands.keys() + [m for n, m in encoders]:
msg = messages_by_name.get(msgname, msgname) msg = messages_by_name.get(msgname, msgname)
if msg not in msg_to_id: if msg not in msg_to_id:
msgid += 1 msgid += 1
msg_to_id[msg] = msgid msg_to_id[msg] = msgid
# Create message definitions # Create message definitions
all_param_types = {} all_param_types = {}
parsercode = build_parsers(parsers, msg_to_id, all_param_types) parsercode = build_encoders(encoders, msg_to_id, all_param_types)
static_strings_code = build_static_strings(static_strings)
# Create command definitions # Create command definitions
cmd_by_id = dict((msg_to_id[messages_by_name.get(msgname, msgname)], cmd) cmd_by_id = dict((msg_to_id[messages_by_name.get(msgname, msgname)], cmd)
for msgname, cmd in commands.items()) for msgname, cmd in commands.items())
@ -297,7 +342,8 @@ def main():
, static_strings, constants, version) , static_strings, constants, version)
# Write output # Write output
f = open(outcfile, 'wb') f = open(outcfile, 'wb')
f.write(FILEHEADER + paramcode + parsercode + cmdcode + icode) f.write(FILEHEADER + paramcode + parsercode + static_strings_code
+ cmdcode + icode)
f.close() f.close()
# Write data dictionary # Write data dictionary

View File

@ -112,7 +112,7 @@ static uint8_t in_sendf;
// Encode a message and transmit it // Encode a message and transmit it
void void
_sendf(uint8_t parserid, ...) _sendf(const struct command_encoder *ce, ...)
{ {
if (readb(&in_sendf)) if (readb(&in_sendf))
// This sendf call was made from an irq handler while the main // This sendf call was made from an irq handler while the main
@ -120,8 +120,7 @@ _sendf(uint8_t parserid, ...)
return; return;
writeb(&in_sendf, 1); writeb(&in_sendf, 1);
const struct command_encoder *cp = &command_encoders[parserid]; uint8_t max_size = READP(ce->max_size);
uint8_t max_size = READP(cp->max_size);
char *buf = console_get_output(max_size + MESSAGE_MIN); char *buf = console_get_output(max_size + MESSAGE_MIN);
if (!buf) if (!buf)
goto done; goto done;
@ -129,10 +128,10 @@ _sendf(uint8_t parserid, ...)
if (max_size) { if (max_size) {
char *maxend = &p[max_size]; char *maxend = &p[max_size];
va_list args; va_list args;
va_start(args, parserid); va_start(args, ce);
uint8_t num_params = READP(cp->num_params); uint8_t num_params = READP(ce->num_params);
const uint8_t *param_types = READP(cp->param_types); const uint8_t *param_types = READP(ce->param_types);
*p++ = READP(cp->msg_id); *p++ = READP(ce->msg_id);
while (num_params--) { while (num_params--) {
if (p > maxend) if (p > maxend)
goto error; goto error;

View File

@ -1,10 +1,9 @@
#ifndef __COMMAND_H #ifndef __COMMAND_H
#define __COMMAND_H #define __COMMAND_H
#include <stdarg.h> // va_list #include <stddef.h>
#include <stddef.h> // size_t
#include <stdint.h> // uint8_t #include <stdint.h> // uint8_t
#include "compiler.h" // __section #include "ctr.h" // DECL_CTR
// Declare a function to run when the specified command is received // Declare a function to run when the specified command is received
#define DECL_COMMAND(FUNC, MSG) \ #define DECL_COMMAND(FUNC, MSG) \
@ -25,7 +24,7 @@
// Declare a message type and transmit it. // Declare a message type and transmit it.
#define sendf(FMT, args...) \ #define sendf(FMT, args...) \
_sendf(_DECL_PARSER(FMT) , ##args) _sendf(_DECL_ENCODER(FMT) , ##args )
// Shut down the machine (also declares a static string to transmit) // Shut down the machine (also declares a static string to transmit)
#define shutdown(msg) \ #define shutdown(msg) \
@ -34,7 +33,8 @@
sched_try_shutdown(_DECL_STATIC_STR(msg)) sched_try_shutdown(_DECL_STATIC_STR(msg))
// command.c // command.c
void _sendf(uint8_t parserid, ...); struct command_encoder;
void _sendf(const struct command_encoder *ce, ...);
// out/compile_time_request.c (auto generated file) // out/compile_time_request.c (auto generated file)
struct command_encoder { struct command_encoder {
@ -50,41 +50,30 @@ enum {
PT_uint32, PT_int32, PT_uint16, PT_int16, PT_byte, PT_uint32, PT_int32, PT_uint16, PT_int16, PT_byte,
PT_string, PT_progmem_buffer, PT_buffer, PT_string, PT_progmem_buffer, PT_buffer,
}; };
extern const struct command_encoder command_encoders[];
extern const struct command_parser * const command_index[]; extern const struct command_parser * const command_index[];
extern const uint8_t command_index_size; extern const uint8_t command_index_size;
extern const uint8_t command_identify_data[]; extern const uint8_t command_identify_data[];
extern const uint32_t command_identify_size; extern const uint32_t command_identify_size;
const struct command_encoder *ctr_lookup_encoder(const char *str);
const struct command_encoder *ctr_lookup_output(const char *str);
uint8_t ctr_lookup_static_string(const char *str);
// Compiler glue for DECL_COMMAND macros above.
#define _DECL_COMMAND(FUNC, FLAGS, MSG) \ #define _DECL_COMMAND(FUNC, FLAGS, MSG) \
char __PASTE(_DECLS_ ## FUNC ## _, __LINE__) [] \ DECL_CTR("_DECL_COMMAND " __stringify(FUNC) " " __stringify(FLAGS) " " MSG)
__visible __section(".compile_time_request") \
= "_DECL_COMMAND " __stringify(FUNC) " " __stringify(FLAGS) " " MSG; \
void __visible FUNC(uint32_t*)
#define _DECL_CONSTANT(NAME, VALUE) \ #define _DECL_CONSTANT(NAME, VALUE) \
char __PASTE(_DECLC_ ## NAME ## _, __LINE__) [] \ DECL_CTR("_DECL_CONSTANT " __stringify(NAME) " " __stringify(VALUE))
__visible __section(".compile_time_request") \
= "_DECL_CONSTANT " __stringify(NAME) " " __stringify(VALUE)
// Create a compile time request and return a unique (incrementing id) #define _DECL_ENCODER(FMT) ({ \
// for that request. DECL_CTR("_DECL_ENCODER " FMT); \
#define _DECL_REQUEST_ID(REQUEST, ID_SECTION) ({ \ ctr_lookup_encoder(FMT); })
static char __PASTE(_DECLS_, __LINE__)[] \
__section(".compile_time_request") = REQUEST; \
asm volatile("" : : "i"(__PASTE(_DECLS_, __LINE__))); \
static char __PASTE(_DECLI_, __LINE__) \
__section(".compile_time_request." ID_SECTION); \
(size_t)&__PASTE(_DECLI_, __LINE__); })
#define _DECL_PARSER(FMT) \ #define _DECL_OUTPUT(FMT) ({ \
_DECL_REQUEST_ID("_DECL_PARSER " FMT, "parsers") DECL_CTR("_DECL_OUTPUT " FMT); \
ctr_lookup_output(FMT); })
#define _DECL_OUTPUT(FMT) \ #define _DECL_STATIC_STR(MSG) ({ \
_DECL_REQUEST_ID("_DECL_OUTPUT " FMT, "parsers") DECL_CTR("_DECL_STATIC_STR " MSG); \
ctr_lookup_static_string(MSG); })
#define _DECL_STATIC_STR(FMT) \
_DECL_REQUEST_ID("_DECL_STATIC_STR " FMT, "static_strings")
#endif // command.h #endif // command.h

17
src/ctr.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef __CTR_H
#define __CTR_H
// Definitions for creating compile time requests. The DECL_CTR macro
// produces requests (text strings) that are placed in a special
// section of the intermediate object files. The requests are then
// extracted during the build and passed to scripts/buildcommand.py.
// The scripts/buildcommand.py code then generates
// out/compile_time_request.c from these requests.
#include "compiler.h" // __section
// Declare a compile time request
#define DECL_CTR(REQUEST) \
static char __PASTE(_DECLS_, __LINE__)[] __attribute__((used)) \
__section(".compile_time_request") = (REQUEST)
#endif // ctr.h

View File

@ -24,11 +24,4 @@ SECTIONS
DECLWRAPPER(taskfuncs) DECLWRAPPER(taskfuncs)
DECLWRAPPER(initfuncs) DECLWRAPPER(initfuncs)
DECLWRAPPER(shutdownfuncs) DECLWRAPPER(shutdownfuncs)
.compile_time_request.static_strings 0 (INFO) : {
*( .compile_time_request.static_strings )
}
.compile_time_request.parsers 0 (INFO) : {
*( .compile_time_request.parsers )
}
} }