vvp: Handle null-bytes in the string literal VPI support

The VPI API for string literals does not correctly handle the case where a
null-byte ('\0') appears in the string literal. It uses strlen() to
calculate the length of the literal, which will give the wrong result if
there is a null-byte in the string literal. Instead of using strlen() use
the stored length to fix this.

In addition when formatting a string literal as a string ignore any
null-bytes. The LRM is not entirely clear what should happen to null-bytes
when formatting a value as a string. But the behavior of ignoring the
null-bytes is consistent with the rules of SystemVerilog for converting a
string literal to a SV string.

This problem can occur when a string literal gets null-byte left-padded due
to width of its context of its expression, but then optimization removes
part of the expression and only leaves the padded string literal.

E.g.
```
$display(0 ? "Yes" : "No");
```

will be transformed into

```
$display("\000No");
```

There is also one subtle change in behavior associated with this. The empty
string ("") is supposed to be equivalent to 8'h00. So e.g.
`$display(":%s:", "")` should print ": :" since the width of the empty
string is 1 byte and the %s modifier prints a string with the width of the
value, left-padding with spaces if necessary. The current implementation
will print "::" though. This change requires to update the marco_with_args
gold file.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2022-12-23 20:57:31 -08:00
parent 23e51ef7a8
commit b8ddeb8848
2 changed files with 17 additions and 4 deletions

View File

@ -1,4 +1,4 @@
first..last first,last last..first
(a)..(c) (a,b,c) (c)..(a)
(a )..(c ) (a,b,c) (c )..(a )
sumsqr(3,4) = 25
sumsqr(5,12) = 169

View File

@ -93,7 +93,7 @@ int __vpiStringConst::vpi_get(int code)
{
switch (code) {
case vpiSize:
return strlen(value_)*8;
return value_len_ * 8;
case vpiSigned:
return 0;
@ -121,7 +121,7 @@ void __vpiStringConst::vpi_get_value(p_vpi_value vp)
{
unsigned uint_value;
p_vpi_vecval vecp;
unsigned size = strlen(value_);
unsigned size = value_len_;
char*rbuf = 0;
char*cp;
@ -131,9 +131,22 @@ void __vpiStringConst::vpi_get_value(p_vpi_value vp)
vp->format = vpiStringVal;
// fallthrough
case vpiStringVal:
cp = value_;
rbuf = (char *) need_result_buf(size + 1, RBUF_VAL);
strcpy(rbuf, value_);
vp->value.str = rbuf;
for (unsigned int i = 0; i < size; i++) {
// Ignore leading null-bytes and replace other null-bytes with space.
// The LRM is not entirely clear on how null bytes should be handled.
// This is the implementation chosen for iverilog.
if (*cp)
*rbuf++ = *cp;
else if (rbuf != vp->value.str)
*rbuf++ = ' ';
cp++;
}
*rbuf = '\0';
break;
case vpiDecStrVal: