diff mbox series

[1/1] include: Auto-generate the sizes lookup table

Message ID 20190102110804.18155-2-lbloch@janustech.com (mailing list archive)
State New, archived
Headers show
Series include: Auto-generate the sizes lookup table | expand

Commit Message

Leonid Bloch Jan. 2, 2019, 11:09 a.m. UTC
The lookup table for power-of-two sizes is now auto-generated during the
build, and not hard-coded into the units.h file.

Signed-off-by: Leonid Bloch <lbloch@janustech.com>
---
 .gitignore           |  1 +
 Makefile             |  5 +++
 block/qcow2.h        |  2 +-
 block/vdi.c          |  1 +
 include/qemu/units.h | 73 --------------------------------------------
 scripts/gen-sizes.sh | 66 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 74 insertions(+), 74 deletions(-)
 create mode 100755 scripts/gen-sizes.sh

Comments

Alberto Garcia Jan. 3, 2019, 9:19 a.m. UTC | #1
On Wed 02 Jan 2019 12:09:05 PM CET, Leonid Bloch wrote:
> +print_sizes() {
> +    local p=10
> +    while [ ${p} -lt 64 ]
> +    do
> +        local pad=' '
> +        local n=$((p % 10))
> +        n=$((1 << n))
> +        [ $((n / 100)) -eq 0 ] && pad='  '
> +        [ $((n / 10)) -eq 0 ] && pad='   '
> +        local suff=$((p / 10))
> +        printf "#define S_%u%s%s%20u\n" ${n} "$(size_suffix ${suff})" \
> +            "${pad}" $((1 << p))
> +        p=$((p + 1))
> +    done
> +}

I have to say that I'm not very convinced of the benefits of replacing a
set of trivial numeric macros with a longer and harder to read shell
script accompanied by changes to the build system.

Berto
Philippe Mathieu-Daudé Jan. 3, 2019, 9:33 a.m. UTC | #2
Hi Leonid,

On 1/2/19 12:09 PM, Leonid Bloch wrote:
> The lookup table for power-of-two sizes is now auto-generated during the
> build, and not hard-coded into the units.h file.
> 

This partially reverts commit 540b8492618eb.

> Signed-off-by: Leonid Bloch <lbloch@janustech.com>
> ---
>  .gitignore           |  1 +
>  Makefile             |  5 +++
>  block/qcow2.h        |  2 +-
>  block/vdi.c          |  1 +
>  include/qemu/units.h | 73 --------------------------------------------
>  scripts/gen-sizes.sh | 66 +++++++++++++++++++++++++++++++++++++++
>  6 files changed, 74 insertions(+), 74 deletions(-)
>  create mode 100755 scripts/gen-sizes.sh
> 
> diff --git a/.gitignore b/.gitignore
> index 0430257313..5945220303 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -59,6 +59,7 @@
>  /qemu-version.h
>  /qemu-version.h.tmp
>  /module_block.h
> +/include/qemu/sizes.h
>  /scsi/qemu-pr-helper
>  /vhost-user-scsi
>  /vhost-user-blk
> diff --git a/Makefile b/Makefile
> index dd53965f77..435d92821b 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -122,6 +122,8 @@ endif
>  
>  GENERATED_FILES += module_block.h
>  
> +GENERATED_FILES += $(SRC_PATH)/include/qemu/sizes.h

You shouldn't write to SRC_PATH (think of read-only filesystem).

Using the current directory is OK (the build system assumes it is
writeable).

So as 'module_block.h', why not simply name it 'lookup_table_sizes.h'?

Actually, we could move those in a generated/ subdirectory, this
would make the source file more explicit:

#include "generated/module_block.h"

Kevin, what do you think?

> +
>  TRACE_HEADERS = trace-root.h $(trace-events-subdirs:%=%/trace.h)
>  TRACE_SOURCES = trace-root.c $(trace-events-subdirs:%=%/trace.c)
>  TRACE_DTRACE =
> @@ -499,6 +501,9 @@ ifdef CONFIG_MPATH
>  scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist
>  endif
>  
> +$(SRC_PATH)/include/qemu/sizes.h: $(SRC_PATH)/scripts/gen-sizes.sh

lookup_table_sizes.h: $(SRC_PATH)/scripts/gen-sizes.sh

> +	$(call quiet-command,sh $(SRC_PATH)/scripts/gen-sizes.sh $@,"GEN","$@")
> +
>  qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
>  	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
>  
> diff --git a/block/qcow2.h b/block/qcow2.h
> index a98d24500b..41d14baa17 100644
> --- a/block/qcow2.h
> +++ b/block/qcow2.h
> @@ -27,7 +27,7 @@
>  
>  #include "crypto/block.h"
>  #include "qemu/coroutine.h"
> -#include "qemu/units.h"
> +#include "qemu/sizes.h"

#include "lookup_table_sizes.h"

>  
>  //#define DEBUG_ALLOC
>  //#define DEBUG_ALLOC2
> diff --git a/block/vdi.c b/block/vdi.c
> index 2380daa583..62dafe0cc6 100644
> --- a/block/vdi.c
> +++ b/block/vdi.c
> @@ -51,6 +51,7 @@
>  
>  #include "qemu/osdep.h"
>  #include "qemu/units.h"
> +#include "qemu/sizes.h"

#include "lookup_table_sizes.h"

>  #include "qapi/error.h"
>  #include "qapi/qobject-input-visitor.h"
>  #include "qapi/qapi-visit-block-core.h"
> diff --git a/include/qemu/units.h b/include/qemu/units.h
> index 1c959d182e..692db3fbb2 100644
> --- a/include/qemu/units.h
> +++ b/include/qemu/units.h
> @@ -17,77 +17,4 @@
>  #define PiB     (INT64_C(1) << 50)
>  #define EiB     (INT64_C(1) << 60)
>  
> -/*
> - * The following lookup table is intended to be used when a literal string of
> - * the number of bytes is required (for example if it needs to be stringified).
> - * It can also be used for generic shortcuts of power-of-two sizes.
> - * This table is generated using the AWK script below:
> - *
> - *  BEGIN {
> - *      suffix="KMGTPE";
> - *      for(i=10; i<64; i++) {
> - *          val=2**i;
> - *          s=substr(suffix, int(i/10), 1);
> - *          n=2**(i%10);
> - *          pad=21-int(log(n)/log(10));
> - *          printf("#define S_%d%siB %*d\n", n, s, pad, val);
> - *      }
> - *  }
> - */
> -
> -#define S_1KiB                  1024
> -#define S_2KiB                  2048
> -#define S_4KiB                  4096
> -#define S_8KiB                  8192
> -#define S_16KiB                16384
> -#define S_32KiB                32768
> -#define S_64KiB                65536
> -#define S_128KiB              131072
> -#define S_256KiB              262144
> -#define S_512KiB              524288
> -#define S_1MiB               1048576
> -#define S_2MiB               2097152
> -#define S_4MiB               4194304
> -#define S_8MiB               8388608
> -#define S_16MiB             16777216
> -#define S_32MiB             33554432
> -#define S_64MiB             67108864
> -#define S_128MiB           134217728
> -#define S_256MiB           268435456
> -#define S_512MiB           536870912
> -#define S_1GiB            1073741824
> -#define S_2GiB            2147483648
> -#define S_4GiB            4294967296
> -#define S_8GiB            8589934592
> -#define S_16GiB          17179869184
> -#define S_32GiB          34359738368
> -#define S_64GiB          68719476736
> -#define S_128GiB        137438953472
> -#define S_256GiB        274877906944
> -#define S_512GiB        549755813888
> -#define S_1TiB         1099511627776
> -#define S_2TiB         2199023255552
> -#define S_4TiB         4398046511104
> -#define S_8TiB         8796093022208
> -#define S_16TiB       17592186044416
> -#define S_32TiB       35184372088832
> -#define S_64TiB       70368744177664
> -#define S_128TiB     140737488355328
> -#define S_256TiB     281474976710656
> -#define S_512TiB     562949953421312
> -#define S_1PiB      1125899906842624
> -#define S_2PiB      2251799813685248
> -#define S_4PiB      4503599627370496
> -#define S_8PiB      9007199254740992
> -#define S_16PiB    18014398509481984
> -#define S_32PiB    36028797018963968
> -#define S_64PiB    72057594037927936
> -#define S_128PiB  144115188075855872
> -#define S_256PiB  288230376151711744
> -#define S_512PiB  576460752303423488
> -#define S_1EiB   1152921504606846976
> -#define S_2EiB   2305843009213693952
> -#define S_4EiB   4611686018427387904
> -#define S_8EiB   9223372036854775808
> -
>  #endif
> diff --git a/scripts/gen-sizes.sh b/scripts/gen-sizes.sh
> new file mode 100755
> index 0000000000..e388164c32
> --- /dev/null
> +++ b/scripts/gen-sizes.sh
> @@ -0,0 +1,66 @@
> +#!/bin/sh
> +
> +size_suffix() {
> +    case ${1} in
> +        1)
> +            printf "KiB"
> +            ;;
> +        2)
> +            printf "MiB"
> +            ;;
> +        3)
> +            printf "GiB"
> +            ;;
> +        4)
> +            printf "TiB"
> +            ;;
> +        5)
> +            printf "PiB"
> +            ;;
> +        6)
> +            printf "EiB"
> +            ;;
> +    esac
> +}
> +
> +print_sizes() {
> +    local p=10
> +    while [ ${p} -lt 64 ]
> +    do
> +        local pad=' '
> +        local n=$((p % 10))
> +        n=$((1 << n))
> +        [ $((n / 100)) -eq 0 ] && pad='  '
> +        [ $((n / 10)) -eq 0 ] && pad='   '
> +        local suff=$((p / 10))
> +        printf "#define S_%u%s%s%20u\n" ${n} "$(size_suffix ${suff})" \
> +            "${pad}" $((1 << p))
> +        p=$((p + 1))
> +    done
> +}
> +
> +print_header() {
> +    cat <<EOF
> +/*
> + * The following lookup table is intended to be used when a literal string of
> + * the number of bytes is required (for example if it needs to be stringified).
> + * It can also be used for generic shortcuts of power-of-two sizes.
> + * This table is generated automatically during the build.
> + *
> + * Author:
> + *   Leonid Bloch  <lbloch@janustech.com>
> + */
> +
> +#ifndef QEMU_SIZES_H
> +#define QEMU_SIZES_H
> +
> +EOF
> +}
> +
> +print_footer() {
> +    printf "\n#endif\n"

       printf "\n#endif /* QEMU_SIZES_H */\n"

> +}
> +
> +print_header  > "${1}"
> +print_sizes  >> "${1}"
> +print_footer >> "${1}"
> 

Regards,

Phil.
Leonid Bloch Jan. 3, 2019, 8:57 p.m. UTC | #3
Hi Berto,

On 1/3/19 12:19 PM, Alberto Garcia wrote:
> On Wed 02 Jan 2019 12:09:05 PM CET, Leonid Bloch wrote:
>> +print_sizes() {
>> +    local p=10
>> +    while [ ${p} -lt 64 ]
>> +    do
>> +        local pad=' '
>> +        local n=$((p % 10))
>> +        n=$((1 << n))
>> +        [ $((n / 100)) -eq 0 ] && pad='  '
>> +        [ $((n / 10)) -eq 0 ] && pad='   '
>> +        local suff=$((p / 10))
>> +        printf "#define S_%u%s%s%20u\n" ${n} "$(size_suffix ${suff})" \
>> +            "${pad}" $((1 << p))
>> +        p=$((p + 1))
>> +    done
>> +}
> 
> I have to say that I'm not very convinced of the benefits of replacing a
> set of trivial numeric macros with a longer and harder to read shell
> script accompanied by changes to the build system.

I think that the benefit is that the script is easily verifiable, 
whereas if someone would like to verify the table, they will need to 
generate it themselves. Also, this table is automatically generated 
anyway, so it only makes sense to generate it during the build.


Leonid.

> 
> Berto
>
Eric Blake Jan. 3, 2019, 9:04 p.m. UTC | #4
On 1/3/19 2:57 PM, Leonid Bloch wrote:

>> I have to say that I'm not very convinced of the benefits of replacing a
>> set of trivial numeric macros with a longer and harder to read shell
>> script accompanied by changes to the build system.
> 
> I think that the benefit is that the script is easily verifiable, 
> whereas if someone would like to verify the table, they will need to 
> generate it themselves. Also, this table is automatically generated 
> anyway, so it only makes sense to generate it during the build.

But no one is submitting patches to active modify the table.  The work
has already been done once to generate it, and the reviewers have
already verified it; at this point, no further changes are likely to
happen (other than my pipe dream of entirely getting rid of the table in
favor of using runtime generation of human-friendly default strings is
added to QemuOpts instead).  If the table were not already in git, then
generating it at build time might make sense; but at this stage in the
game, you're slowing down every build to regenerate something that is
already correct.
Leonid Bloch Jan. 3, 2019, 9:06 p.m. UTC | #5
Hi Phil,

Thanks for your review! Very valid points. All taken, except the naming 
(I think "pow2_sizes.h" would be better than "lookup_table_sizes.h", 
no?) and the "generated" directory - I agree that it's a good idea, but 
I think it requires a separate patch which moves the generated files there.

Regards,
Leonid.

On 1/3/19 12:33 PM, Philippe Mathieu-Daudé wrote:
> 
> So as 'module_block.h', why not simply name it 'lookup_table_sizes.h'?
> 
> Actually, we could move those in a generated/ subdirectory, this
> would make the source file more explicit:
> 
> #include "generated/module_block.h"
> 
> Kevin, what do you think?
>
Leonid Bloch Jan. 3, 2019, 9:21 p.m. UTC | #6
Hi,

On 1/4/19 12:04 AM, Eric Blake wrote:
> On 1/3/19 2:57 PM, Leonid Bloch wrote:
> 
>>> I have to say that I'm not very convinced of the benefits of replacing a
>>> set of trivial numeric macros with a longer and harder to read shell
>>> script accompanied by changes to the build system.
>>
>> I think that the benefit is that the script is easily verifiable,
>> whereas if someone would like to verify the table, they will need to
>> generate it themselves. Also, this table is automatically generated
>> anyway, so it only makes sense to generate it during the build.
> 
> But no one is submitting patches to active modify the table.  The work
> has already been done once to generate it, and the reviewers have
> already verified it; at this point, no further changes are likely to
> happen (other than my pipe dream of entirely getting rid of the table in
> favor of using runtime generation of human-friendly default strings is
> added to QemuOpts instead).  If the table were not already in git, then
> generating it at build time might make sense; but at this stage in the
> game, you're slowing down every build to regenerate something that is
> already correct.
> 

OK, that's a good point. But still, I think that you agree that it would 
be more correct to generate it during the build, right? So now it is 
there already and it works. But isn't it worth it to make it more correct?

Leonid.
Eric Blake Jan. 3, 2019, 9:42 p.m. UTC | #7
On 1/3/19 3:21 PM, Leonid Bloch wrote:
> Hi,
> 
> On 1/4/19 12:04 AM, Eric Blake wrote:
>> On 1/3/19 2:57 PM, Leonid Bloch wrote:
>>
>>>> I have to say that I'm not very convinced of the benefits of replacing a
>>>> set of trivial numeric macros with a longer and harder to read shell
>>>> script accompanied by changes to the build system.
>>>
>>> I think that the benefit is that the script is easily verifiable,
>>> whereas if someone would like to verify the table, they will need to
>>> generate it themselves. Also, this table is automatically generated
>>> anyway, so it only makes sense to generate it during the build.
>>
>> But no one is submitting patches to active modify the table.  The work
>> has already been done once to generate it, and the reviewers have
>> already verified it; at this point, no further changes are likely to
>> happen (other than my pipe dream of entirely getting rid of the table in
>> favor of using runtime generation of human-friendly default strings is
>> added to QemuOpts instead).  If the table were not already in git, then
>> generating it at build time might make sense; but at this stage in the
>> game, you're slowing down every build to regenerate something that is
>> already correct.
>>
> 
> OK, that's a good point. But still, I think that you agree that it would 
> be more correct to generate it during the build, right? So now it is 
> there already and it works. But isn't it worth it to make it more correct?

I said "might make sense", not "I would have done it that way".  I also
don't see how a generated table is "more correct" than a hand-written
one - they are both equally correct if all values in the table match
what they are documented to provide.

In my view, code generators make sense when used on code that is
expected to change over time (a good example is QAPI because we add new
commands every release; other places might be a generator to help deal
with syscall handlers since newer kernels can add a syscall; or even the
fact that we have used some powerful GNU make textual processing to make
it easy to add files to particular subsets of the build with as few
lines edited as possible), where the goal is that the generator gives
you both a compact representation that is easier to edit, and that the
expansion from the generator ensures that repetitive boilerplate is
formed without typos.  In short, if a generator results in a net
reduction in lines of edited source in relation to the lines it
produces, AND if the source that gets regenerated is likely to change,
then it makes total sense to spend time on the generator.  But when the
amount of effort to write a generator costs as much as just hard-coding
the list outright, especially when the list is not going to change
(there really aren't any other powers of 2 within 64 bits), I'm not sure
a generator adds any value.  Unfortunately, your patch diffstat of:

>  .gitignore           |  1 +
>  Makefile             |  5 +++
>  block/qcow2.h        |  2 +-
>  block/vdi.c          |  1 +
>  include/qemu/units.h | 73 --------------------------------------------
>  scripts/gen-sizes.sh | 66 +++++++++++++++++++++++++++++++++++++++
>  6 files changed, 74 insertions(+), 74 deletions(-)
>  create mode 100755 scripts/gen-sizes.sh

is a wash (you did not result in any fewer lines in the codebase
overall), and the commit message did not do a good job saying WHY we
need it (you said WHAT it does - avoiding a hard-coded list - but not
WHY the hardcoded list is so bad that a generator would be better).  So
I'm not seeing the point of this patch.
Alberto Garcia Jan. 4, 2019, 9:31 a.m. UTC | #8
On Thu 03 Jan 2019 10:42:30 PM CET, Eric Blake wrote:

> In my view, code generators make sense when used on code that is
> expected to change over time (a good example is QAPI because we add
> new commands every release; other places might be a generator to help
> deal with syscall handlers since newer kernels can add a syscall; or
> even the fact that we have used some powerful GNU make textual
> processing to make it easy to add files to particular subsets of the
> build with as few lines edited as possible), where the goal is that
> the generator gives you both a compact representation that is easier
> to edit, and that the expansion from the generator ensures that
> repetitive boilerplate is formed without typos.  In short, if a
> generator results in a net reduction in lines of edited source in
> relation to the lines it produces, AND if the source that gets
> regenerated is likely to change, then it makes total sense to spend
> time on the generator.  But when the amount of effort to write a
> generator costs as much as just hard-coding the list outright,
> especially when the list is not going to change (there really aren't
> any other powers of 2 within 64 bits), I'm not sure a generator adds
> any value.

I agree with Eric.

Berto
Leonid Bloch Jan. 4, 2019, 9:23 p.m. UTC | #9
On 1/4/19 12:31 PM, Alberto Garcia wrote:
> On Thu 03 Jan 2019 10:42:30 PM CET, Eric Blake wrote:
> 
>> In my view, code generators make sense when used on code that is
>> expected to change over time (a good example is QAPI because we add
>> new commands every release; other places might be a generator to help
>> deal with syscall handlers since newer kernels can add a syscall; or
>> even the fact that we have used some powerful GNU make textual
>> processing to make it easy to add files to particular subsets of the
>> build with as few lines edited as possible), where the goal is that
>> the generator gives you both a compact representation that is easier
>> to edit, and that the expansion from the generator ensures that
>> repetitive boilerplate is formed without typos.  In short, if a
>> generator results in a net reduction in lines of edited source in
>> relation to the lines it produces, AND if the source that gets
>> regenerated is likely to change, then it makes total sense to spend
>> time on the generator.  But when the amount of effort to write a
>> generator costs as much as just hard-coding the list outright,
>> especially when the list is not going to change (there really aren't
>> any other powers of 2 within 64 bits), I'm not sure a generator adds
>> any value.
> 
> I agree with Eric.

Fine with me. I just thought that in the previous conversation 
(https://patchwork.kernel.org/patch/10666975/#22302435) we have agreed 
that I'll send this patch. I've sent v2 already with Phil's suggestions 
included, please feel free to pull it if desired.

Leonid.

> 
> Berto
>
diff mbox series

Patch

diff --git a/.gitignore b/.gitignore
index 0430257313..5945220303 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,6 +59,7 @@ 
 /qemu-version.h
 /qemu-version.h.tmp
 /module_block.h
+/include/qemu/sizes.h
 /scsi/qemu-pr-helper
 /vhost-user-scsi
 /vhost-user-blk
diff --git a/Makefile b/Makefile
index dd53965f77..435d92821b 100644
--- a/Makefile
+++ b/Makefile
@@ -122,6 +122,8 @@  endif
 
 GENERATED_FILES += module_block.h
 
+GENERATED_FILES += $(SRC_PATH)/include/qemu/sizes.h
+
 TRACE_HEADERS = trace-root.h $(trace-events-subdirs:%=%/trace.h)
 TRACE_SOURCES = trace-root.c $(trace-events-subdirs:%=%/trace.c)
 TRACE_DTRACE =
@@ -499,6 +501,9 @@  ifdef CONFIG_MPATH
 scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist
 endif
 
+$(SRC_PATH)/include/qemu/sizes.h: $(SRC_PATH)/scripts/gen-sizes.sh
+	$(call quiet-command,sh $(SRC_PATH)/scripts/gen-sizes.sh $@,"GEN","$@")
+
 qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
 
diff --git a/block/qcow2.h b/block/qcow2.h
index a98d24500b..41d14baa17 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -27,7 +27,7 @@ 
 
 #include "crypto/block.h"
 #include "qemu/coroutine.h"
-#include "qemu/units.h"
+#include "qemu/sizes.h"
 
 //#define DEBUG_ALLOC
 //#define DEBUG_ALLOC2
diff --git a/block/vdi.c b/block/vdi.c
index 2380daa583..62dafe0cc6 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -51,6 +51,7 @@ 
 
 #include "qemu/osdep.h"
 #include "qemu/units.h"
+#include "qemu/sizes.h"
 #include "qapi/error.h"
 #include "qapi/qobject-input-visitor.h"
 #include "qapi/qapi-visit-block-core.h"
diff --git a/include/qemu/units.h b/include/qemu/units.h
index 1c959d182e..692db3fbb2 100644
--- a/include/qemu/units.h
+++ b/include/qemu/units.h
@@ -17,77 +17,4 @@ 
 #define PiB     (INT64_C(1) << 50)
 #define EiB     (INT64_C(1) << 60)
 
-/*
- * The following lookup table is intended to be used when a literal string of
- * the number of bytes is required (for example if it needs to be stringified).
- * It can also be used for generic shortcuts of power-of-two sizes.
- * This table is generated using the AWK script below:
- *
- *  BEGIN {
- *      suffix="KMGTPE";
- *      for(i=10; i<64; i++) {
- *          val=2**i;
- *          s=substr(suffix, int(i/10), 1);
- *          n=2**(i%10);
- *          pad=21-int(log(n)/log(10));
- *          printf("#define S_%d%siB %*d\n", n, s, pad, val);
- *      }
- *  }
- */
-
-#define S_1KiB                  1024
-#define S_2KiB                  2048
-#define S_4KiB                  4096
-#define S_8KiB                  8192
-#define S_16KiB                16384
-#define S_32KiB                32768
-#define S_64KiB                65536
-#define S_128KiB              131072
-#define S_256KiB              262144
-#define S_512KiB              524288
-#define S_1MiB               1048576
-#define S_2MiB               2097152
-#define S_4MiB               4194304
-#define S_8MiB               8388608
-#define S_16MiB             16777216
-#define S_32MiB             33554432
-#define S_64MiB             67108864
-#define S_128MiB           134217728
-#define S_256MiB           268435456
-#define S_512MiB           536870912
-#define S_1GiB            1073741824
-#define S_2GiB            2147483648
-#define S_4GiB            4294967296
-#define S_8GiB            8589934592
-#define S_16GiB          17179869184
-#define S_32GiB          34359738368
-#define S_64GiB          68719476736
-#define S_128GiB        137438953472
-#define S_256GiB        274877906944
-#define S_512GiB        549755813888
-#define S_1TiB         1099511627776
-#define S_2TiB         2199023255552
-#define S_4TiB         4398046511104
-#define S_8TiB         8796093022208
-#define S_16TiB       17592186044416
-#define S_32TiB       35184372088832
-#define S_64TiB       70368744177664
-#define S_128TiB     140737488355328
-#define S_256TiB     281474976710656
-#define S_512TiB     562949953421312
-#define S_1PiB      1125899906842624
-#define S_2PiB      2251799813685248
-#define S_4PiB      4503599627370496
-#define S_8PiB      9007199254740992
-#define S_16PiB    18014398509481984
-#define S_32PiB    36028797018963968
-#define S_64PiB    72057594037927936
-#define S_128PiB  144115188075855872
-#define S_256PiB  288230376151711744
-#define S_512PiB  576460752303423488
-#define S_1EiB   1152921504606846976
-#define S_2EiB   2305843009213693952
-#define S_4EiB   4611686018427387904
-#define S_8EiB   9223372036854775808
-
 #endif
diff --git a/scripts/gen-sizes.sh b/scripts/gen-sizes.sh
new file mode 100755
index 0000000000..e388164c32
--- /dev/null
+++ b/scripts/gen-sizes.sh
@@ -0,0 +1,66 @@ 
+#!/bin/sh
+
+size_suffix() {
+    case ${1} in
+        1)
+            printf "KiB"
+            ;;
+        2)
+            printf "MiB"
+            ;;
+        3)
+            printf "GiB"
+            ;;
+        4)
+            printf "TiB"
+            ;;
+        5)
+            printf "PiB"
+            ;;
+        6)
+            printf "EiB"
+            ;;
+    esac
+}
+
+print_sizes() {
+    local p=10
+    while [ ${p} -lt 64 ]
+    do
+        local pad=' '
+        local n=$((p % 10))
+        n=$((1 << n))
+        [ $((n / 100)) -eq 0 ] && pad='  '
+        [ $((n / 10)) -eq 0 ] && pad='   '
+        local suff=$((p / 10))
+        printf "#define S_%u%s%s%20u\n" ${n} "$(size_suffix ${suff})" \
+            "${pad}" $((1 << p))
+        p=$((p + 1))
+    done
+}
+
+print_header() {
+    cat <<EOF
+/*
+ * The following lookup table is intended to be used when a literal string of
+ * the number of bytes is required (for example if it needs to be stringified).
+ * It can also be used for generic shortcuts of power-of-two sizes.
+ * This table is generated automatically during the build.
+ *
+ * Author:
+ *   Leonid Bloch  <lbloch@janustech.com>
+ */
+
+#ifndef QEMU_SIZES_H
+#define QEMU_SIZES_H
+
+EOF
+}
+
+print_footer() {
+    printf "\n#endif\n"
+}
+
+print_header  > "${1}"
+print_sizes  >> "${1}"
+print_footer >> "${1}"