From b8ddeb88480b6087528be6405183496af4d527c2 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 23 Dec 2022 20:57:31 -0800 Subject: [PATCH] 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 --- ivtest/gold/macro_with_args.gold | 2 +- vvp/vpi_const.cc | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ivtest/gold/macro_with_args.gold b/ivtest/gold/macro_with_args.gold index c13571aa5..b0a34d36c 100644 --- a/ivtest/gold/macro_with_args.gold +++ b/ivtest/gold/macro_with_args.gold @@ -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 diff --git a/vvp/vpi_const.cc b/vvp/vpi_const.cc index 3fccb82b8..175c4e5fe 100644 --- a/vvp/vpi_const.cc +++ b/vvp/vpi_const.cc @@ -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: